From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/nthals/halx86/i386/halnls.h | 29 + private/ntos/nthals/halx86/i386/halp.h | 428 +++++ private/ntos/nthals/halx86/i386/ix8259.inc | 199 ++ private/ntos/nthals/halx86/i386/ixbeep.asm | 181 ++ private/ntos/nthals/halx86/i386/ixbusdat.c | 373 ++++ private/ntos/nthals/halx86/i386/ixclock.asm | 760 ++++++++ private/ntos/nthals/halx86/i386/ixcmos.asm | 1135 ++++++++++++ private/ntos/nthals/halx86/i386/ixcmos.inc | 66 + private/ntos/nthals/halx86/i386/ixdat.c | 159 ++ private/ntos/nthals/halx86/i386/ixenvirv.c | 183 ++ private/ntos/nthals/halx86/i386/ixfirm.c | 79 + private/ntos/nthals/halx86/i386/ixhwsup.c | 758 ++++++++ private/ntos/nthals/halx86/i386/ixidle.asm | 88 + private/ntos/nthals/halx86/i386/ixinfo.c | 285 +++ private/ntos/nthals/halx86/i386/ixipi.asm | 188 ++ private/ntos/nthals/halx86/i386/ixirql.asm | 1297 +++++++++++++ private/ntos/nthals/halx86/i386/ixisa.h | 110 ++ private/ntos/nthals/halx86/i386/ixisabus.c | 640 +++++++ private/ntos/nthals/halx86/i386/ixisasup.c | 1980 ++++++++++++++++++++ private/ntos/nthals/halx86/i386/ixkdcom.c | 684 +++++++ private/ntos/nthals/halx86/i386/ixkdcom.h | 165 ++ private/ntos/nthals/halx86/i386/ixlock.asm | 475 +++++ private/ntos/nthals/halx86/i386/ixmca.c | 868 +++++++++ private/ntos/nthals/halx86/i386/ixmcaa.asm | 420 +++++ private/ntos/nthals/halx86/i386/ixnmi.c | 137 ++ private/ntos/nthals/halx86/i386/ixpcibrd.c | 1029 +++++++++++ private/ntos/nthals/halx86/i386/ixpcibus.c | 2520 ++++++++++++++++++++++++++ private/ntos/nthals/halx86/i386/ixpciint.c | 458 +++++ private/ntos/nthals/halx86/i386/ixphwsup.c | 535 ++++++ private/ntos/nthals/halx86/i386/ixproc.c | 160 ++ private/ntos/nthals/halx86/i386/ixprofil.asm | 437 +++++ private/ntos/nthals/halx86/i386/ixreboot.c | 143 ++ private/ntos/nthals/halx86/i386/ixstall.asm | 480 +++++ private/ntos/nthals/halx86/i386/ixswint.asm | 327 ++++ private/ntos/nthals/halx86/i386/ixsysbus.c | 188 ++ private/ntos/nthals/halx86/i386/ixsysint.asm | 582 ++++++ private/ntos/nthals/halx86/i386/ixthunk.c | 82 + private/ntos/nthals/halx86/i386/ixusage.c | 535 ++++++ private/ntos/nthals/halx86/i386/ncrdetct.c | 253 +++ private/ntos/nthals/halx86/i386/pcip.h | 186 ++ private/ntos/nthals/halx86/i386/xxbiosa.asm | 712 ++++++++ private/ntos/nthals/halx86/i386/xxbiosc.c | 299 +++ private/ntos/nthals/halx86/i386/xxdisp.c | 476 +++++ private/ntos/nthals/halx86/i386/xxflshbf.c | 52 + private/ntos/nthals/halx86/i386/xxhal.c | 462 +++++ private/ntos/nthals/halx86/i386/xxioacc.asm | 386 ++++ private/ntos/nthals/halx86/i386/xxkdsup.c | 404 +++++ private/ntos/nthals/halx86/i386/xxmemory.c | 368 ++++ private/ntos/nthals/halx86/i386/xxstubs.c | 131 ++ private/ntos/nthals/halx86/i386/xxtime.c | 91 + 50 files changed, 22983 insertions(+) create mode 100644 private/ntos/nthals/halx86/i386/halnls.h create mode 100644 private/ntos/nthals/halx86/i386/halp.h create mode 100644 private/ntos/nthals/halx86/i386/ix8259.inc create mode 100644 private/ntos/nthals/halx86/i386/ixbeep.asm create mode 100644 private/ntos/nthals/halx86/i386/ixbusdat.c create mode 100644 private/ntos/nthals/halx86/i386/ixclock.asm create mode 100644 private/ntos/nthals/halx86/i386/ixcmos.asm create mode 100644 private/ntos/nthals/halx86/i386/ixcmos.inc create mode 100644 private/ntos/nthals/halx86/i386/ixdat.c create mode 100644 private/ntos/nthals/halx86/i386/ixenvirv.c create mode 100644 private/ntos/nthals/halx86/i386/ixfirm.c create mode 100644 private/ntos/nthals/halx86/i386/ixhwsup.c create mode 100644 private/ntos/nthals/halx86/i386/ixidle.asm create mode 100644 private/ntos/nthals/halx86/i386/ixinfo.c create mode 100644 private/ntos/nthals/halx86/i386/ixipi.asm create mode 100644 private/ntos/nthals/halx86/i386/ixirql.asm create mode 100644 private/ntos/nthals/halx86/i386/ixisa.h create mode 100644 private/ntos/nthals/halx86/i386/ixisabus.c create mode 100644 private/ntos/nthals/halx86/i386/ixisasup.c create mode 100644 private/ntos/nthals/halx86/i386/ixkdcom.c create mode 100644 private/ntos/nthals/halx86/i386/ixkdcom.h create mode 100644 private/ntos/nthals/halx86/i386/ixlock.asm create mode 100644 private/ntos/nthals/halx86/i386/ixmca.c create mode 100644 private/ntos/nthals/halx86/i386/ixmcaa.asm create mode 100644 private/ntos/nthals/halx86/i386/ixnmi.c create mode 100644 private/ntos/nthals/halx86/i386/ixpcibrd.c create mode 100644 private/ntos/nthals/halx86/i386/ixpcibus.c create mode 100644 private/ntos/nthals/halx86/i386/ixpciint.c create mode 100644 private/ntos/nthals/halx86/i386/ixphwsup.c create mode 100644 private/ntos/nthals/halx86/i386/ixproc.c create mode 100644 private/ntos/nthals/halx86/i386/ixprofil.asm create mode 100644 private/ntos/nthals/halx86/i386/ixreboot.c create mode 100644 private/ntos/nthals/halx86/i386/ixstall.asm create mode 100644 private/ntos/nthals/halx86/i386/ixswint.asm create mode 100644 private/ntos/nthals/halx86/i386/ixsysbus.c create mode 100644 private/ntos/nthals/halx86/i386/ixsysint.asm create mode 100644 private/ntos/nthals/halx86/i386/ixthunk.c create mode 100644 private/ntos/nthals/halx86/i386/ixusage.c create mode 100644 private/ntos/nthals/halx86/i386/ncrdetct.c create mode 100644 private/ntos/nthals/halx86/i386/pcip.h create mode 100644 private/ntos/nthals/halx86/i386/xxbiosa.asm create mode 100644 private/ntos/nthals/halx86/i386/xxbiosc.c create mode 100644 private/ntos/nthals/halx86/i386/xxdisp.c create mode 100644 private/ntos/nthals/halx86/i386/xxflshbf.c create mode 100644 private/ntos/nthals/halx86/i386/xxhal.c create mode 100644 private/ntos/nthals/halx86/i386/xxioacc.asm create mode 100644 private/ntos/nthals/halx86/i386/xxkdsup.c create mode 100644 private/ntos/nthals/halx86/i386/xxmemory.c create mode 100644 private/ntos/nthals/halx86/i386/xxstubs.c create mode 100644 private/ntos/nthals/halx86/i386/xxtime.c (limited to 'private/ntos/nthals/halx86/i386') diff --git a/private/ntos/nthals/halx86/i386/halnls.h b/private/ntos/nthals/halx86/i386/halnls.h new file mode 100644 index 000000000..56dc7d557 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/halnls.h @@ -0,0 +1,29 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + halnls.h + +Abstract: + + Strings which are used in the HAL + + English + +--*/ + +#define MSG_HARDWARE_ERROR1 "\n*** Hardware Malfunction\n\n" +#define MSG_HARDWARE_ERROR2 "Call your hardware vendor for support\n\n" +#define MSG_HALT "\n*** The system has halted ***\n" +#define MSG_NMI_PARITY "NMI: Parity Check / Memory Parity Error\n" +#define MSG_NMI_CHANNEL_CHECK "NMI: Channel Check / IOCHK\n" +#define MSG_NMI_FAIL_SAFE "NMI: Fail-safe timer\n" +#define MSG_NMI_BUS_TIMEOUT "NMI: Bus Timeout\n" +#define MSG_NMI_SOFTWARE_NMI "NMI: Software NMI generated\n" +#define MSG_NMI_EISA_IOCHKERR "NMI: Eisa IOCHKERR board %\n" + +#define MSG_DEBUG_ENABLE "Kernel Debugger Using: COM%x (Port 0x%x, Baud Rate %d)\n" +#define MSG_DEBUG_9600 "Switching debugger to 9600 baud\n" +#define MSG_MCE_PENDING "Machine Check Exception pending, MCE exceptions not enabled\n" diff --git a/private/ntos/nthals/halx86/i386/halp.h b/private/ntos/nthals/halx86/i386/halp.h new file mode 100644 index 000000000..fb3fed047 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/halp.h @@ -0,0 +1,428 @@ +/*++ BUILD Version: 0001 // Increment this if a change has global effects + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + halp.h + +Abstract: + + This header file defines the private Hardware Architecture Layer (HAL) + interfaces, defines and structures. + +Author: + + John Vert (jvert) 11-Feb-92 + + +Revision History: + +--*/ + +#ifndef _HALP_ +#define _HALP_ +#include "nthal.h" +#include "hal.h" +#include "halnls.h" + +#ifndef _HALI_ +#include "..\inc\hali.h" +#endif + +#ifdef RtlMoveMemory +#undef RtlMoveMemory +#undef RtlCopyMemory +#undef RtlFillMemory +#undef RtlZeroMemory + +#define RtlCopyMemory(Destination,Source,Length) RtlMoveMemory((Destination),(Source),(Length)) +VOID +RtlMoveMemory ( + PVOID Destination, + CONST VOID *Source, + ULONG Length + ); + +VOID +RtlFillMemory ( + PVOID Destination, + ULONG Length, + UCHAR Fill + ); + +VOID +RtlZeroMemory ( + PVOID Destination, + ULONG Length + ); + +#endif + +#if MCA + +#include "ixmca.h" + +#else + +#include "ixisa.h" + +#endif + +#include "ix8259.inc" + +// +// Define map register translation entry structure. +// + +typedef struct _TRANSLATION_ENTRY { + PVOID VirtualAddress; + ULONG PhysicalAddress; + ULONG Index; +} TRANSLATION_ENTRY, *PTRANSLATION_ENTRY; + +// +// Some devices require a phyicially contiguous data buffers for DMA transfers. +// Map registers are used give the appearance that all data buffers are +// contiguous. In order to pool all of the map registers a master +// adapter object is used. This object is allocated and saved internal to this +// file. It contains a bit map for allocation of the registers and a queue +// for requests which are waiting for more map registers. This object is +// allocated during the first request to allocate an adapter which requires +// map registers. +// +// In this system, the map registers are translation entries which point to +// map buffers. Map buffers are physically contiguous and have physical memory +// addresses less than 0x01000000. All of the map registers are allocated +// initialially; however, the map buffers are allocated base in the number of +// adapters which are allocated. +// +// If the master adapter is NULL in the adapter object then device does not +// require any map registers. +// + +extern PADAPTER_OBJECT MasterAdapterObject; + +extern POBJECT_TYPE *IoAdapterObjectType; + +extern BOOLEAN LessThan16Mb; + +extern BOOLEAN HalpEisaDma; + +// +// Map buffer prameters. These are initialized in HalInitSystem +// + +extern PHYSICAL_ADDRESS HalpMapBufferPhysicalAddress; +extern ULONG HalpMapBufferSize; + +extern ULONG HalpBusType; +extern ULONG HalpCpuType; +extern UCHAR HalpSerialLen; +extern UCHAR HalpSerialNumber[]; + +// +// The following macros are taken from mm\i386\mi386.h. We need them here +// so the HAL can map its own memory before memory-management has been +// initialized, or during a BugCheck. +// + +#define PTE_BASE ((ULONG)0xC0000000) +#define PDE_BASE ((ULONG)0xC0300000) + +// +// MiGetPdeAddress returns the address of the PDE which maps the +// given virtual address. +// + +#define MiGetPdeAddress(va) ((PHARDWARE_PTE)(((((ULONG)(va)) >> 22) << 2) + PDE_BASE)) + +// +// MiGetPteAddress returns the address of the PTE which maps the +// given virtual address. +// + +#define MiGetPteAddress(va) ((PHARDWARE_PTE)(((((ULONG)(va)) >> 12) << 2) + PTE_BASE)) + +// +// Resource usage information +// + +#pragma pack(1) +typedef struct { + UCHAR Flags; + KIRQL Irql; + UCHAR BusReleativeVector; +} IDTUsage; + +typedef struct _HalAddressUsage{ + struct _HalAddressUsage *Next; + CM_RESOURCE_TYPE Type; // Port or Memory + UCHAR Flags; // same as IDTUsage.Flags + struct { + ULONG Start; + ULONG Length; + } Element[]; +} ADDRESS_USAGE; +#pragma pack() + +#define IDTOwned 0x01 // IDT is not available for others +#define InterruptLatched 0x02 // Level or Latched +#define InternalUsage 0x11 // Report usage on internal bus +#define DeviceUsage 0x21 // Report usage on device bus + +extern IDTUsage HalpIDTUsage[]; +extern ADDRESS_USAGE *HalpAddressUsageList; + +#define HalpRegisterAddressUsage(a) \ + (a)->Next = HalpAddressUsageList, HalpAddressUsageList = (a); + +// +// Temp definitions to thunk into supporting new bus extension format +// + +VOID +HalpRegisterInternalBusHandlers ( + VOID + ); + +PBUS_HANDLER +HalpAllocateBusHandler ( + IN INTERFACE_TYPE InterfaceType, + IN BUS_DATA_TYPE BusDataType, + IN ULONG BusNumber, + IN INTERFACE_TYPE ParentBusDataType, + IN ULONG ParentBusNumber, + IN ULONG BusSpecificData + ); + +#define HalpHandlerForBus HaliHandlerForBus +#define HalpSetBusHandlerParent(c,p) (c)->ParentHandler = p; + +// +// Define function prototypes. +// + +VOID +HalInitSystemPhase2( + VOID + ); + +KIRQL +HaliRaiseIrqlToDpcLevel ( + VOID + ); + +BOOLEAN +HalpGrowMapBuffers( + PADAPTER_OBJECT AdapterObject, + ULONG Amount + ); + +PADAPTER_OBJECT +HalpAllocateAdapter( + IN ULONG MapRegistersPerChannel, + IN PVOID AdapterBaseVa, + IN PVOID MapRegisterBase + ); + +VOID +HalpClockInterrupt( + VOID + ); + +VOID +HalpDisableAllInterrupts ( + VOID + ); + +VOID +HalpProfileInterrupt( + VOID + ); + +VOID +HalpInitializeClock( + VOID + ); + +VOID +HalpInitializeDisplay( + VOID + ); + +VOID +HalpInitializeStallExecution( + IN CCHAR ProcessorNumber + ); + +VOID +HalpInitializePICs( + VOID + ); + +VOID +HalpIrq13Handler ( + VOID + ); + +VOID +HalpFlushTLB ( + VOID + ); + +VOID +HalpSerialize ( + VOID + ); + +PVOID +HalpMapPhysicalMemory( + IN PVOID PhysicalAddress, + IN ULONG NumberPages + ); + +PVOID +HalpMapPhysicalMemoryWriteThrough( + IN PVOID PhysicalAddress, + IN ULONG NumberPages + ); + +ULONG +HalpAllocPhysicalMemory( + IN PLOADER_PARAMETER_BLOCK LoaderBlock, + IN ULONG MaxPhysicalAddress, + IN ULONG NoPages, + IN BOOLEAN bAlignOn64k + ); + +VOID +HalpBiosDisplayReset( + IN VOID + ); + +HAL_DISPLAY_BIOS_INFORMATION +HalpGetDisplayBiosInformation ( + VOID + ); + +VOID +HalpDisplayDebugStatus( + IN PUCHAR Status, + IN ULONG Length + ); + +VOID +HalpInitializeCmos ( + VOID + ); + +VOID +HalpReadCmosTime ( + PTIME_FIELDS TimeFields + ); + +VOID +HalpWriteCmosTime ( + PTIME_FIELDS TimeFields + ); + +VOID +HalpAcquireCmosSpinLock ( + VOID + ); + +VOID +HalpReleaseCmosSpinLock ( + VOID + ); + +VOID +HalpResetAllProcessors ( + VOID + ); + +VOID +HalpCpuID ( + ULONG InEax, + PULONG OutEax, + PULONG OutEbx, + PULONG OutEcx, + PULONG OutEdx + ); + +ULONGLONG +FASTCALL +RDMSR ( + IN ULONG MsrAddress + ); + +VOID +WRMSR ( + IN ULONG MsrAddress, + IN ULONGLONG MsrValue + ); + +VOID +HalpEnableInterruptHandler ( + IN UCHAR ReportFlags, + IN ULONG BusInterruptVector, + IN ULONG SystemInterruptVector, + IN KIRQL SystemIrql, + IN VOID (*HalInterruptServiceRoutine)(VOID), + IN KINTERRUPT_MODE InterruptMode + ); + +VOID +HalpRegisterVector ( + IN UCHAR ReportFlags, + IN ULONG BusInterruptVector, + IN ULONG SystemInterruptVector, + IN KIRQL SystemIrql + ); + +VOID +HalpReportResourceUsage ( + IN PUNICODE_STRING HalName, + IN INTERFACE_TYPE DeviceInterfaceToUse + ); + + +// +// Defines for HalpFeatureBits +// + +#define HAL_PERF_EVENTS 0x00000001 +#define HAL_NO_SPECULATION 0x00000002 +#define HAL_MCA_PRESENT 0x00000004 // Intel MCA Available +#define HAL_MCE_PRESENT 0x00000008 // ONLY Pentium style MCE available + +extern ULONG HalpFeatureBits; + +// +// Defines for Processor Features returned from CPUID instruction +// + +#define CPUID_MCA_MASK 0x4000 +#define CPUID_MCE_MASK 0x0080 + + +NTSTATUS +HalpGetMcaLog( + OUT PMCA_EXCEPTION Exception, + OUT PULONG ReturnedLength + ); + +NTSTATUS +HalpMcaRegisterDriver( + IN PMCA_DRIVER_INFO pMcaDriverInfo // Info about registering driver + ); + +VOID +HalpMcaInit( + VOID + ); + + + +#endif // _HALP_ diff --git a/private/ntos/nthals/halx86/i386/ix8259.inc b/private/ntos/nthals/halx86/i386/ix8259.inc new file mode 100644 index 000000000..d053fbc22 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ix8259.inc @@ -0,0 +1,199 @@ +;/* +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; ix8259.inc +; +; Abstract: +; +; This module contains the definitions used by HAL to manipulate +; 8259 interrupt controller and 8259-specific constants. +; +; WARNING: This file is included by both ASM and C files. +; +; Author: +; +; John Vert (jvert) 31-Dec-1991 +; +; (Moved from ke\i386\kimacro.inc) +; +;-- +if 0 ; Begin C only code */ + +// +// 8259 defines for C code +// BE SURE TO CHANGE THESE VALUES IN BOTH TABLES! +// + +#define HIGHEST_LEVEL_FOR_8259 27 // Highest level for standard 8259 +#define PRIMARY_VECTOR_BASE 0x30 // Vector base for standard 8259 +#define PROFILE_VECTOR (PRIMARY_VECTOR_BASE + 8) // standard profile +#define CLOCK_VECTOR (PRIMARY_VECTOR_BASE + 0) // standard clock +#define I386_80387_IRQ 0x0d // standard npx +#define I386_80387_IRQL (HIGHEST_LEVEL_FOR_8259 - I386_80387_IRQ) +#define I386_80387_VECTOR (PRIMARY_VECTOR_BASE + I386_80387_IRQ) +#define PIC_SLAVE_IRQ 2 +#define V2I(a) (a-PRIMARY_VECTOR_BASE) // Vector to interrupt macro + +/* +endif + +; +; Same 8259 defines for assemble code +; BE SURE TO CHANGE THESE VALUES IN BOTH TABLES! +; + +HIGHEST_LEVEL_FOR_8259 equ 27 +PRIMARY_VECTOR_BASE equ 30h +PROFILE_VECTOR equ (PRIMARY_VECTOR_BASE + 8) +CLOCK_VECTOR equ (PRIMARY_VECTOR_BASE + 0) +I386_80387_IRQ equ 0dh +I386_80387_IRQL equ (HIGHEST_LEVEL_FOR_8259 - I386_80387_IRQ) +I386_80387_VECTOR equ (PRIMARY_VECTOR_BASE + I386_80387_IRQ) +I386_80387_BUSY_PORT equ 0f0h ; port to dismiss busy error line +PIC_SLAVE_IRQ equ 2 + + +; +; The rest of the file are macros used in assemble only. +; + +;++ +; +; SET_8259_MASK +; +; Macro Description: +; +; This macro sets 8259 interrupt mask register with the mask +; passed from eax register. +; +; Note: Currently, only two 8259s are support. As a result, +; only ax contains valid mask. +; +; Arguments: +; +; (eax) = mask for setting 8259 interrupt mask register +; +;-- + +SET_8259_MASK macro +local a ; define local labels + + out PIC1_PORT1, al ; set master 8259 mask + shr eax, 8 ; shift slave 8259 mask to al + out PIC2_PORT1, al ; set slave 8259 mask +a: +endm + +; +; Interrupt controller register addresses +; + +PIC1_PORT0 equ 020H +PIC1_PORT1 equ 021H +PIC2_PORT0 equ 0A0H +PIC2_PORT1 equ 0A1H + +; +; Commands for Interrupt controller +; + +PIC1_EOI_MASK equ 060H +PIC2_EOI equ 062H +OCW2_NON_SPECIFIC_EOI equ 020H +OCW2_SPECIFIC_EOI equ 060H +OCW3_READ_ISR equ 0BH +OCW3_READ_IRR equ 0AH + + +;++ +; +; IODELAY +; +; Macro Description: +; +; This macro simply does a jmp to next instruction to synchronize +; IO port access. +; +; Arguments: +; +; None +; +;-- + +IODELAY macro + jmp $+2 +endm + + +;++ +; +; PICDELAY +; +; Macro Description: +; +; This macro does an inb on interrupt mask register to provide the +; time for 8259 to get stabled. +; +; Why do we need this? +; +; This is because: +; . The 80386 has a delayed write to memory and delayed output to IO +; capability and +; . 8259 needs some time to settle +; +; It is possible for the actual output cycle to 8259 to occur after +; the completion of instructions following the out instruction. For +; example, the STI instruction after SET_MASK and dismiss interrupt +; macros may complete before 8259 actually drops the interrupt. We don't +; want this happen in MCA system. +; +; You may argue that most OEMS add about 450ns delay to solve the +; back-to-back IO (delay) problem. But, remember that STI is not an IO +; instruction. +; +; Arguments: +; +; None +; +; NOTE: * The content of AL will be destroyed on return. +; +;-- + +PIC1DELAY macro + in al, PIC1_PORT1 +endm + +PIC2DELAY macro + in al, PIC2_PORT1 +endm + +;++ +; +; SOFT_INTERRUPT_EXIT +; +; Macro Description: +; +; This macro is executed on return from the soft interrupt +; service routine. Its function is to restore privileged processor +; state, and continue thread execution. +; +; Arguments: +; +; (TOS) = previous irql +; (TOS+4 ...) = machine_state frame +; +;-- + +SOFT_INTERRUPT_EXIT macro + + EXTRNP _HalpEndSoftwareInterrupt,1 + cli + call _HalpEndSoftwareInterrupt@4 ; restore irql + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without EOI +endm +;*/ + diff --git a/private/ntos/nthals/halx86/i386/ixbeep.asm b/private/ntos/nthals/halx86/i386/ixbeep.asm new file mode 100644 index 000000000..0722ca9df --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixbeep.asm @@ -0,0 +1,181 @@ + title "Hal Beep" +;++ +; +;Copyright (c) 1991 Microsoft Corporation +; +;Module Name: +; +; ixbeep.asm +; +;Abstract: +; +; HAL routine to make noise. It needs to synchronize its access to the +; 8254, since we also use the 8254 for the profiling interrupt. +; +; +;Author: +; +; John Vert (jvert) 31-Jul-1991 +; +;Revision History: +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include mac386.inc + .list + + extrn _Halp8254Lock:DWORD + +; +; Defines used to program the i8254 for the speaker. +; + +I8254_TIMER_CONTROL_PORT EQU 43h +I8254_TIMER_DATA_PORT EQU 42h +I8254_TIMER_CLOCK_IN EQU 1193167 +I8254_TIMER_TONE_MAX EQU 65536 +I8254_TIMER_CONTROL_SELECT EQU 0B6h +SPEAKER_CONTROL_PORT EQU 61h +SPEAKER_OFF_MASK EQU 0FCh +SPEAKER_ON_MASK EQU 03h + +_TEXT$03 SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "HalMakeBeep" +;++ +; +; BOOLEAN +; HalMakeBeep( +; IN ULONG Frequency +; ) +; +; Routine Description: +; +; This function sets the frequency of the speaker, causing it to sound a +; tone. The tone will sound until the speaker is explicitly turned off, +; so the driver is responsible for controlling the duration of the tone. +; +;Arguments: +; +; Frequency - Supplies the frequency of the desired tone. A frequency of +; 0 means the speaker should be shut off. +; +;Return Value: +; +; TRUE - Operation was successful (frequency within range or zero) +; FALSE - Operation was unsuccessful (frequency was out of range) +; Current tone (if any) is unchanged. +; +;-- + +Frequency equ [ebp + 8] + +cPublicProc _HalMakeBeep , 1 + + push ebp ; save ebp + mov ebp, esp ; + + push ebx ; save ebx +Hmb10: pushfd ; save flags + cli ; disable interrupts + +ifndef NT_UP + lea eax, _Halp8254Lock + ACQUIRE_SPINLOCK eax,Hmb99 +endif + + ; + ; Stop the speaker. + ; + + in al, SPEAKER_CONTROL_PORT + jmp $+2 + and al, SPEAKER_OFF_MASK + out SPEAKER_CONTROL_PORT, al + jmp $+2 + + ; + ; Calculate Tone: Tone = 1.193MHz / Frequency. + ; N.B. Tone must fit in 16 bits. + ; + + mov ecx, DWORD PTR [Frequency] ; ecx <- frequency + or ecx, ecx ; (ecx) == 0? + je SHORT Hmb30 ; goto Hmb30 + + mov eax, I8254_TIMER_CLOCK_IN ; eax <- 1.193MHz, the clockin + ; for the speaker tone + sub edx, edx ; edx <- zero + div ecx ; eax <- 1.193MHz / frequency + cmp eax, I8254_TIMER_TONE_MAX ; (eax) < 2**16? + jb SHORT Hmb20 ; goto Hmb20 + + ; + ; Invalid frequency. Return FALSE. + ; + + sub al, al + jmp SHORT Hmb40 +Hmb20: + ; + ; Program the 8254 with the calculated tone. + ; + + push eax ; save Tone + mov al, I8254_TIMER_CONTROL_SELECT + out I8254_TIMER_CONTROL_PORT, al ; select timer control register + jmp $+2 + + pop eax ; restore Tone + out I8254_TIMER_DATA_PORT, al ; program 8254 with Tone lsb + jmp $+2 + mov al, ah + out I8254_TIMER_DATA_PORT, al ; program 8254 with Tone msb + jmp $+2 + + ; + ; Turn the speaker on. + ; + + in al, SPEAKER_CONTROL_PORT + jmp $+2 + or al, SPEAKER_ON_MASK + out SPEAKER_CONTROL_PORT, al + jmp $+2 + +Hmb30: + ; + ; Return TRUE. + ; + + mov al, 1 + +Hmb40: +ifndef NT_UP + lea ebx, _Halp8254Lock + RELEASE_SPINLOCK ebx +endif + + popfd + pop ebx ; restore ebx + pop ebp ; restore ebp + stdRET _HalMakeBeep + +ifndef NT_UP + +Hmb99: popfd + SPIN_ON_SPINLOCK eax, + +endif + +stdENDP _HalMakeBeep + +_TEXT$03 ends + end diff --git a/private/ntos/nthals/halx86/i386/ixbusdat.c b/private/ntos/nthals/halx86/i386/ixbusdat.c new file mode 100644 index 000000000..ca7205c68 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixbusdat.c @@ -0,0 +1,373 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixhwsup.c + +Abstract: + + This module contains the IoXxx routines for the NT I/O system that + are hardware dependent. Were these routines not hardware dependent, + they would reside in the iosubs.c module. + +Author: + + Ken Reneris (kenr) July-28-1994 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "halp.h" + + +VOID HalpInitOtherBuses (VOID); + + +ULONG +HalpNoBusData ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +ULONG HalpcGetCmosData ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +ULONG HalpcSetCmosData ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +ULONG HalpGetCmosData ( + IN ULONG BusNumber, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Length + ); + +ULONG HalpSetCmosData ( + IN ULONG BusNumber, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Length + ); + +HalpGetEisaData ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + + +// +// Prototype for system bus handlers +// + +NTSTATUS +HalpAdjustEisaResourceList ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList + ); + +ULONG +HalpGetSystemInterruptVector ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +ULONG +HalpGetEisaInterruptVector ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +BOOLEAN +HalpTranslateSystemBusAddress ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ); + +BOOLEAN +HalpTranslateIsaBusAddress ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ); + +BOOLEAN +HalpTranslateEisaBusAddress ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ); + +VOID +HalpRegisterInternalBusHandlers ( + VOID + ); + +NTSTATUS +HalpHibernateHal ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler + ); + +NTSTATUS +HalpResumeHal ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler + ); + +#ifdef MCA +// +// Default functionality of MCA handlers is the same as the Eisa handlers, +// just use them +// + +#define HalpGetMCAInterruptVector HalpGetEisaInterruptVector +#define HalpAdjustMCAResourceList HalpAdjustEisaResourceList; + +HalpGetPosData ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + + +#endif + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,HalpRegisterInternalBusHandlers) +#pragma alloc_text(INIT,HalpAllocateBusHandler) +#endif + + +VOID +HalpRegisterInternalBusHandlers ( + VOID + ) +{ + PBUS_HANDLER Bus; + + if (KeGetCurrentPrcb()->Number) { + // only need to do this once + return ; + } + + // + // Initalize BusHandler data before registering any handlers + // + + HalpInitBusHandler (); + + // + // Build internal-bus 0, or system level bus + // + + Bus = HalpAllocateBusHandler ( + Internal, + ConfigurationSpaceUndefined, + 0, // Internal BusNumber 0 + InterfaceTypeUndefined, // no parent bus + 0, + 0 // no bus specfic data + ); + + Bus->GetInterruptVector = HalpGetSystemInterruptVector; + Bus->TranslateBusAddress = HalpTranslateSystemBusAddress; + +#if 0 + // + // Hibernate and resume the hal by getting notifications + // for when this bus is hibernated or resumed. Since it's + // the first bus to be added, it will be the last to hibernate + // and the first to resume + // + + Bus->HibernateBus = HalpHibernateHal; + Bus->ResumeBus = HalpResumeHal; +#endif + + // + // Add handlers for Cmos config space. + // + + Bus = HalpAllocateBusHandler (InterfaceTypeUndefined, Cmos, 0, -1, 0, 0); + Bus->GetBusData = HalpcGetCmosData; + Bus->SetBusData = HalpcSetCmosData; + + Bus = HalpAllocateBusHandler (InterfaceTypeUndefined, Cmos, 1, -1, 0, 0); + Bus->GetBusData = HalpcGetCmosData; + Bus->SetBusData = HalpcSetCmosData; + +#ifndef MCA + // + // Build Isa/Eisa bus #0 + // + + Bus = HalpAllocateBusHandler (Eisa, EisaConfiguration, 0, Internal, 0, 0); + Bus->GetBusData = HalpGetEisaData; + Bus->GetInterruptVector = HalpGetEisaInterruptVector; + Bus->AdjustResourceList = HalpAdjustEisaResourceList; + Bus->TranslateBusAddress = HalpTranslateEisaBusAddress; + + Bus = HalpAllocateBusHandler (Isa, ConfigurationSpaceUndefined, 0, Eisa, 0, 0); + Bus->GetBusData = HalpNoBusData; + Bus->BusAddresses->Memory.Limit = 0xFFFFFF; + Bus->TranslateBusAddress = HalpTranslateIsaBusAddress; + +#else + + // + // Build MCA bus #0 + // + + Bus = HalpAllocateBusHandler (MicroChannel, Pos, 0, Internal, 0, 0); + Bus->GetBusData = HalpGetPosData; + Bus->GetInterruptVector = HalpGetMCAInterruptVector; + Bus->AdjustResourceList = HalpAdjustMCAResourceList; + +#endif + + HalpInitOtherBuses (); +} + + + +PBUS_HANDLER +HalpAllocateBusHandler ( + IN INTERFACE_TYPE InterfaceType, + IN BUS_DATA_TYPE BusDataType, + IN ULONG BusNumber, + IN INTERFACE_TYPE ParentBusInterfaceType, + IN ULONG ParentBusNumber, + IN ULONG BusSpecificData + ) +/*++ + +Routine Description: + + Stub function to map old style code into new HalRegisterBusHandler code. + + Note we can add our specific bus handler functions after this bus + handler structure has been added since this is being done during + hal initialization. + +--*/ +{ + PBUS_HANDLER Bus; + + + // + // Create bus handler - new style + // + + HaliRegisterBusHandler ( + InterfaceType, + BusDataType, + BusNumber, + ParentBusInterfaceType, + ParentBusNumber, + BusSpecificData, + NULL, + &Bus + ); + + if (InterfaceType != InterfaceTypeUndefined) { + Bus->BusAddresses = ExAllocatePool (SPRANGEPOOL, sizeof (SUPPORTED_RANGES)); + RtlZeroMemory (Bus->BusAddresses, sizeof (SUPPORTED_RANGES)); + Bus->BusAddresses->Version = BUS_SUPPORTED_RANGE_VERSION; + Bus->BusAddresses->Dma.Limit = 7; + Bus->BusAddresses->Memory.Limit = 0xFFFFFFFF; + Bus->BusAddresses->IO.Limit = 0xFFFF; + Bus->BusAddresses->IO.SystemAddressSpace = 1; + Bus->BusAddresses->PrefetchMemory.Base = 1; + } + + return Bus; +} + + +// +// C to Asm thunks for CMos +// + +ULONG HalpcGetCmosData ( + IN PBUS_HANDLER BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ) +{ + // bugbug: this interface should be rev'ed to support non-zero offsets + if (Offset != 0) { + return 0; + } + + return HalpGetCmosData (BusHandler->BusNumber, SlotNumber, Buffer, Length); +} + + +ULONG HalpcSetCmosData ( + IN PBUS_HANDLER BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ) +{ + // bugbug: this interface should be rev'ed to support non-zero offsets + if (Offset != 0) { + return 0; + } + + return HalpSetCmosData (BusHandler->BusNumber, SlotNumber, Buffer, Length); +} diff --git a/private/ntos/nthals/halx86/i386/ixclock.asm b/private/ntos/nthals/halx86/i386/ixclock.asm new file mode 100644 index 000000000..6abb583cb --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixclock.asm @@ -0,0 +1,760 @@ + + title "Interval Clock Interrupt" +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; ixclock.asm +; +; Abstract: +; +; This module implements the code necessary to field and process the +; interval clock interrupt. +; +; Author: +; +; Shie-Lin Tzong (shielint) 12-Jan-1990 +; +; Environment: +; +; Kernel mode only. +; +; Revision History: +; +; bryanwi 20-Sep-90 +; +; Add KiSetProfileInterval, KiStartProfileInterrupt, +; KiStopProfileInterrupt procedures. +; KiProfileInterrupt ISR. +; KiProfileList, KiProfileLock are delcared here. +; +; shielint 10-Dec-90 +; Add performance counter support. +; Move system clock to irq8, ie we now use RTC to generate system +; clock. Performance count and Profile use timer 1 counter 0. +; The interval of the irq0 interrupt can be changed by +; KiSetProfileInterval. Performance counter does not care about the +; interval of the interrupt as long as it knows the rollover count. +; Note: Currently I implemented 1 performance counter for the whole +; i386 NT. +; +; John Vert (jvert) 11-Jul-1991 +; Moved from ke\i386 to hal\i386. Removed non-HAL stuff +; +; shie-lin tzong (shielint) 13-March-92 +; Move System clock back to irq0 and use RTC (irq8) to generate +; profile interrupt. Performance counter and system clock use time1 +; counter 0 of 8254. +; +; Landy Wang (corollary!landy) 04-Dec-92 +; Move much code into separate modules for easy inclusion by various +; HAL builds. +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\ix8259.inc +include i386\kimacro.inc +include mac386.inc +include i386\ixcmos.inc + .list + + EXTRNP _KeUpdateSystemTime,0 + EXTRNP Kei386EoiHelper,0,IMPORT + EXTRNP _KeSetTimeIncrement,2,IMPORT + EXTRNP _HalEndSystemInterrupt,2 + EXTRNP _HalBeginSystemInterrupt,3 + EXTRNP _HalpReleaseCmosSpinLock ,0 + EXTRNP _HalpMcaQueueDpc, 0 + +; +; Constants used to initialize timer 0 +; + +TIMER1_DATA_PORT0 EQU 40H ; Timer1, channel 0 data port +TIMER1_CONTROL_PORT0 EQU 43H ; Timer1, channel 0 control port +TIMER2_DATA_PORT0 EQU 48H ; Timer1, channel 0 data port +TIMER2_CONTROL_PORT0 EQU 4BH ; Timer1, channel 0 control port +TIMER1_IRQ EQU 0 ; Irq 0 for timer1 interrupt + +COMMAND_8254_COUNTER0 EQU 00H ; Select count 0 +COMMAND_8254_RW_16BIT EQU 30H ; Read/Write LSB firt then MSB +COMMAND_8254_MODE2 EQU 4 ; Use mode 2 +COMMAND_8254_BCD EQU 0 ; Binary count down +COMMAND_8254_LATCH_READ EQU 0 ; Latch read command + +PERFORMANCE_FREQUENCY EQU 1193182 + +; +; ==== Values used for System Clock ==== +; + + +_DATA SEGMENT DWORD PUBLIC 'DATA' + +; +; The following array stores the per microsecond loop count for each +; central processor. +; + +; +; 8254 spinlock. +; + public _Halp8254Lock +_Halp8254Lock dd 0 + + + public HalpPerfCounterLow, HalpPerfCounterHigh + public HalpLastPerfCounterLow, HalpLastPerfCounterHigh +HalpPerfCounterLow dd 0 +HalpPerfCounterHigh dd 0 +HalpLastPerfCounterLow dd 0 +HalpLastPerfCounterHigh dd 0 + + public HalpCurrentRollOver, HalpCurrentTimeIncrement +HalpCurrentRollOver dd 0 +HalpCurrentTimeIncrement dd 0 + + public _HalpClockWork, _HalpClockSetMSRate, _HalpClockMcaQueueDpc +_HalpClockWork label dword + _HalpClockSetMSRate db 0 + _HalpClockMcaQueueDpc db 0 + _bReserved1 db 0 + _bReserved2 db 0 + +; +; Convert the interval to rollover count for 8254 Timer1 device. +; Timer1 counts down a 16 bit value at a rate of 1.193181667M counts-per-sec. +; (The main crystal freq is 14.31818, and this is a divide by 12) +; +; The best fit value closest to 10ms is 10.0144012689ms: +; ROLLOVER_COUNT 11949 +; TIME_INCREMENT 100144 +; Calculated error is -.0109472 s/day +; +; +; The following table contains 8254 values timer values to use at +; any given ms setting from 1ms - 15ms. All values work out to the +; same error per day (-.0109472 s/day). +; + + public HalpRollOverTable + + ; RollOver Time + ; Count Increment MS +HalpRollOverTable dd 1197, 10032 ; 1 ms + dd 2394, 20064 ; 2 ms + dd 3591, 30096 ; 3 ms + dd 4767, 39952 ; 4 ms + dd 5964, 49984 ; 5 ms + dd 7161, 60016 ; 6 ms + dd 8358, 70048 ; 7 ms + dd 9555, 80080 ; 8 ms + dd 10731, 89936 ; 9 ms + dd 11949, 100144 ; 10 ms + dd 13125, 110000 ; 11 ms + dd 14322, 120032 ; 12 ms + dd 15519, 130064 ; 13 ms + dd 16695, 139920 ; 14 ms + dd 17892, 149952 ; 15 ms + +TimeIncr equ 4 +RollOver equ 0 + + public HalpLargestClockMS, HalpNextMSRate, HalpPendingMSRate +HalpLargestClockMS dd 15 ; Table goes to 15MS +HalpNextMSRate dd 0 +HalpPendingMSRate dd 0 + +_DATA ends + + +INIT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "Initialize Clock" +;++ +; +; VOID +; HalpInitializeClock ( +; ) +; +; Routine Description: +; +; This routine initialize system time clock using 8254 timer1 counter 0 +; to generate an interrupt at every 15ms interval at 8259 irq0. +; +; See the definitions of TIME_INCREMENT and ROLLOVER_COUNT if clock rate +; needs to be changed. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- +cPublicProc _HalpInitializeClock ,0 + + mov eax, PCR[PcPrcb] + cmp byte ptr [eax].PbCpuType, 4 ; 486 or better? + jc short @f ; no, skip + + mov HalpLargestClockMS, 10 ; Limit 486's to 10MS +@@: + mov eax, HalpLargestClockMS + mov ecx, HalpRollOverTable.TimeIncr + mov edx, HalpRollOverTable[eax*8-8].TimeIncr + mov eax, HalpRollOverTable[eax*8-8].RollOver + + mov HalpCurrentTimeIncrement, edx + +; +; (ecx) = Min time_incr +; (edx) = Max time_incr +; (eax) = max roll over count +; + + push eax + stdCall _KeSetTimeIncrement, + pop ecx + + pushfd ; save caller's eflag + cli ; make sure interrupts are disabled + +; +; Set clock rate +; (ecx) = RollOverCount +; + + mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2 + out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0 + IoDelay + mov al, cl + out TIMER1_DATA_PORT0, al ; program timer 0 LSB count + IoDelay + mov al,ch + out TIMER1_DATA_PORT0, al ; program timer 0 MSB count + + popfd ; restore caller's eflag + mov HalpCurrentRollOver, ecx ; Set RollOverCount & initialized + + stdRET _HalpInitializeClock + +stdENDP _HalpInitializeClock + +INIT ends + +_TEXT$03 SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "Query Performance Counter" +;++ +; +; LARGE_INTEGER +; KeQueryPerformanceCounter ( +; OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL +; ) +; +; Routine Description: +; +; This routine returns current 64-bit performance counter and, +; optionally, the Performance Frequency. +; +; Note this routine can NOT be called at Profiling interrupt +; service routine. Because this routine depends on IRR0 to determine +; the actual count. +; +; Also note that the performace counter returned by this routine +; is not necessary the value when this routine is just entered. +; The value returned is actually the counter value at any point +; between the routine is entered and is exited. +; +; Arguments: +; +; PerformanceFrequency [TOS+4] - optionally, supplies the address +; of a variable to receive the performance counter frequency. +; +; Return Value: +; +; Current value of the performance counter will be returned. +; +;-- + + +; +; Parameter definitions +; + +KqpcFrequency EQU [esp+12] ; User supplied Performance Frequence + +cPublicProc _KeQueryPerformanceCounter ,1 +; +; First check to see if the performance counter has been initialized yet. +; Since the kernel debugger calls KeQueryPerformanceCounter to support the +; !timer command, we need to return something reasonable before 8254 +; initialization has occured. Reading garbage off the 8254 is not reasonable. +; + cmp HalpCurrentRollOver, 0 + je Kqpc50 + + push ebx + push esi + +Kqpc01: pushfd + cli +Kqpc20: + +; +; Fetch the base value. Note that interrupts are off. +; + + mov ebx, HalpPerfCounterLow + mov esi, HalpPerfCounterHigh ; [esi:ebx] = Performance counter + +; +; Fetch the current counter value from the hardware +; + + mov al, COMMAND_8254_LATCH_READ+COMMAND_8254_COUNTER0 + ;Latch PIT Ctr 0 command. + out TIMER1_CONTROL_PORT0, al + IODelay + in al, TIMER1_DATA_PORT0 ;Read PIT Ctr 0, LSByte. + IODelay + movzx ecx,al ;Zero upper bytes of (ECX). + in al, TIMER1_DATA_PORT0 ;Read PIT Ctr 0, MSByte. + mov ch, al ;(CX) = PIT Ctr 0 count. + +; +; Now enable interrupts such that if timer interrupt is pending, it can +; be serviced and update the PerformanceCounter. Note that there could +; be a long time between the sti and cli because ANY interrupt could come +; in in between. +; + + popfd ; don't re-enable interrupts if + nop ; the caller had them off! + ; (kernel debugger calls this function + ; with interrupts disabled) + + jmp $+2 ; allow interrupt in case counter + ; has wrapped + + pushfd + cli + +; +; Fetch the base value again. +; +; Note: it's possible that the counter wrapped before we read the value +; and that the timer tick interrupt did not occur during while interrupts +; where enabled. (ie, there's a delay between when the device raises the +; interrupt and when the processor see it). +; +; +; note *2 - + + + mov eax, HalpPerfCounterLow + mov edx, HalpPerfCounterHigh ; [edx:eax] = new counter value + +; +; Compare the two reads of Performance counter. If they are different, +; start over +; + + cmp eax, ebx + jne short Kqpc20 + cmp edx, esi + jne short Kqpc20 + + neg ecx ; PIT counts down from 0h + add ecx, HalpCurrentRollOver + jnc short Kqpc60 + +Kqpc30: + add eax, ecx + adc edx, 0 ; [edx:eax] = Final result + + cmp edx, HalpLastPerfCounterHigh + jc short Kqpc70 ; jmp if edx < lastperfcounterhigh + jne short Kqpc35 ; jmp if edx > lastperfcounterhigh + + cmp eax, HalpLastPerfCounterLow + jc short Kqpc70 ; jmp if eax < lastperfcounterlow + +Kqpc35: + mov HalpLastPerfCounterLow, eax + mov HalpLastPerfCounterHigh, edx + + popfd ; restore interrupt flag + +; +; Return the freq. if caller wants it. +; + + cmp dword ptr KqpcFrequency, 0 ; is it a NULL variable? + jz short Kqpc40 ; if z, yes, go exit + + mov ecx, KqpcFrequency ; (ecx)-> Frequency variable + mov DWORD PTR [ecx], PERFORMANCE_FREQUENCY ; Set frequency + mov DWORD PTR [ecx+4], 0 + +Kqpc40: + pop esi ; restore esi and ebx + pop ebx + stdRET _KeQueryPerformanceCounter + + +Kqpc50: +; Initialization hasn't occured yet, so just return zeroes. + mov eax, 0 + mov edx, 0 + stdRET _KeQueryPerformanceCounter + +Kqpc60: +; +; The current count is larger then the HalpCurrentRollOver. The only way +; that could happen is if there is an interrupt in route to the processor +; but it was not processed while interrupts were enabled. +; + mov esi, [esp] ; (esi) = flags + mov ecx, HalpCurrentRollOver ; (ecx) = max possible value + popfd ; restore flags + + test esi, EFLAGS_INTERRUPT_MASK + jnz Kqpc01 ; ints are enabled, problem should go away + + pushfd ; fix stack + jmp short Kqpc30 ; ints are disabled, use max count (ecx) + + +Kqpc70: +; +; The current count is smaller then the last returned count. The only way +; this should occur is if there is an interrupt in route to the processor +; which was not been processed. +; + + mov ebx, HalpLastPerfCounterLow + mov esi, HalpLastPerfCounterHigh + + mov ecx, ebx + or ecx, esi ; is last returned value 0? + jz short Kqpc35 ; Yes, then just return what we have + + ; sanity check - make sure count is not off by bogus amount + sub ebx, eax + sbb esi, edx + jnz short Kqpc75 ; off by bogus amount + cmp ebx, HalpCurrentRollOver + jg short Kqpc75 ; off by bogus amount + + sub eax, ebx + sbb edx, esi ; (edx:eax) = last returned count + + mov ecx, [esp] ; (ecx) = flags + popfd + + test ecx, EFLAGS_INTERRUPT_MASK + jnz Kqpc01 ; ints enabled, problem should go away + + pushfd ; fix stack + jmp Kqpc35 ; ints disabled, just return last count + +Kqpc75: + popfd + xor eax, eax ; reset bogus values + mov HalpLastPerfCounterLow, eax + mov HalpLastPerfCounterHigh, eax + jmp Kqpc01 ; go try again + +stdENDP _KeQueryPerformanceCounter + +;++ +; +; VOID +; HalCalibratePerformanceCounter ( +; IN volatile PLONG Number +; ) +; +; /*++ +; +; Routine Description: +; +; This routine resets the performance counter value for the current +; processor to zero. The reset is done such that the resulting value +; is closely synchronized with other processors in the configuration. +; +; Arguments: +; +; Number - Supplies a pointer to count of the number of processors in +; the configuration. +; +; Return Value: +; +; None. +;-- +cPublicProc _HalCalibratePerformanceCounter,1 + mov eax, [esp+4] ; ponter to Number + pushfd ; save previous interrupt state + cli ; disable interrupts (go to high_level) + + lock dec dword ptr [eax] ; count down + +@@: cmp dword ptr [eax], 0 ; wait for all processors to signal + jnz short @b + + ; + ; Nothing to calibrate on a UP machine... + ; + + popfd ; restore interrupt flag + stdRET _HalCalibratePerformanceCounter + +stdENDP _HalCalibratePerformanceCounter + + + + page ,132 + subttl "System Clock Interrupt" +;++ +; +; Routine Description: +; +; This routine is entered as the result of an interrupt generated by CLOCK. +; Its function is to dismiss the interrupt, raise system Irql to +; CLOCK2_LEVEL, update performance counter and transfer control to the +; standard system routine to update the system time and the execution +; time of the current thread +; and process. +; +; Arguments: +; +; None +; Interrupt is disabled +; +; Return Value: +; +; Does not return, jumps directly to KeUpdateSystemTime, which returns +; +; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt +; +;-- + ENTER_DR_ASSIST Hci_a, Hci_t + +cPublicProc _HalpClockInterrupt ,0 + +; +; Save machine state in trap frame +; + + ENTER_INTERRUPT Hci_a, Hci_t + +; +; (esp) - base of trap frame +; + +ifdef MCA + +; +; Special hack for MCA machines +; + + in al, 61h + jmp $+2 + or al, 80h + out 61h, al + jmp $+2 + +endif ; MCA + + +; +; Dismiss interrupt and raise irq level to clock2 level +; + +Hci10: + push CLOCK_VECTOR + sub esp, 4 ; allocate space to save OldIrql + stdCall _HalBeginSystemInterrupt, + + or al,al ; check for spurious interrupt + jz Hci100 + +; +; Update performance counter +; + + xor ebx, ebx + mov eax, HalpCurrentRollOver + add HalpPerfCounterLow, eax ; update performace counter + adc HalpPerfCounterHigh, ebx + +; +; Check for any more work +; + mov eax, HalpCurrentTimeIncrement + + cmp _HalpClockWork, ebx ; Any clock interrupt work desired? + jz _KeUpdateSystemTime@0 ; No, process tick + + cmp _HalpClockMcaQueueDpc, bl + je short Hci20 + + mov _HalpClockMcaQueueDpc, bl + +; +; Queue MCA Dpc +; + + push eax + stdCall _HalpMcaQueueDpc ; Queue MCA Dpc + pop eax + + +Hci20: +; +; (esp) = OldIrql +; (esp+4) = Vector +; (esp+8) = base of trap frame +; ebp = trap frame +; eax = time increment +; ebx = 0 +; + cmp _HalpClockSetMSRate, bl ; New clock rate desired? + jz _KeUpdateSystemTime@0 ; No, process tick + + +; +; Time of clock frequency is being changed. See if the 8254 was +; was reprogrammed for a new rate during last tick +; + cmp HalpPendingMSRate, ebx ; Was a new rate set durning last + jnz short Hci50 ; tick? Yes, go update globals + +Hci40: +; (eax) = time increment for current tick + +; +; A new clock rate needs to be set. Setting the rate here will +; cause the tick after the next tick to be at the new rate. +; (the next tick is already in progress by the 8254 and will occur +; at the same rate as this tick) +; + mov ebx, HalpNextMSRate + mov HalpPendingMSRate, ebx ; pending rate + + mov ecx, HalpRollOverTable[ebx*8-8].RollOver + +; +; Set clock rate +; (ecx) = RollOverCount +; + push eax ; save current tick's rate + + mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2 + out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0 + IoDelay + mov al, cl + out TIMER1_DATA_PORT0, al ; program timer 0 LSB count + IoDelay + mov al,ch + out TIMER1_DATA_PORT0, al ; program timer 0 MSB count + + pop eax + +; +; (esp) = OldIrql +; (esp+4) = Vector +; (esp+8) = base of trap frame +; ebp = trap frame +; eax = time increment +; + jmp _KeUpdateSystemTime@0 ; dispatch this tick + +Hci50: +; +; The next tick will occur at the rate which was programmed during the last +; tick. Update globals for new rate which starts with the next tick. +; +; (eax) = time increment for current tick +; + mov ebx, HalpPendingMSRate + mov ecx, HalpRollOverTable[ebx*8-8].RollOver + mov edx, HalpRollOverTable[ebx*8-8].TimeIncr + + mov HalpCurrentRollOver, ecx + mov HalpCurrentTimeIncrement, edx ; next tick rate + mov HalpPendingMSRate, 0 ; no longer pending, clear it + + cmp ebx, HalpNextMSRate ; new rate == NextRate? + jne short Hci40 ; no, go set new pending rate + + mov _HalpClockSetMSRate, 0 ; all done setting new rate + jmp _KeUpdateSystemTime@0 ; dispatch this tick + +Hci100: + add esp, 8 ; spurious, no EndOfInterrupt + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi + +stdENDP _HalpClockInterrupt + +;++ +; +; ULONG +; HalSetTimeIncrement ( +; IN ULONG DesiredIncrement +; ) +; +; /*++ +; +; Routine Description: +; +; This routine initialize system time clock to generate an +; interrupt at every DesiredIncrement interval. +; +; Arguments: +; +; DesiredIncrement - desired interval between every timer tick (in +; 100ns unit.) +; +; Return Value: +; +; The *REAL* time increment set. +;-- +cPublicProc _HalSetTimeIncrement,1 + + mov eax, [esp+4] ; desired setting + xor edx, edx + mov ecx, 10000 + div ecx ; round to MS + + cmp eax, HalpLargestClockMS ; MS > max? + jc short @f + mov eax, HalpLargestClockMS ; yes, use max +@@: + or eax, eax ; MS < min? + jnz short @f + inc eax ; yes, use min +@@: + mov HalpNextMSRate, eax + mov _HalpClockSetMSRate, 1 ; New clock rate desired. + + mov eax, HalpRollOverTable[eax*8-8].TimeIncr + stdRET _HalSetTimeIncrement + +stdENDP _HalSetTimeIncrement +_TEXT$03 ends + + end diff --git a/private/ntos/nthals/halx86/i386/ixcmos.asm b/private/ntos/nthals/halx86/i386/ixcmos.asm new file mode 100644 index 000000000..5cc391076 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixcmos.asm @@ -0,0 +1,1135 @@ + title "Cmos Access Routines" +;++ +; +; Module Name: +; +; ixcmos.asm +; +; Abstract: +; +; Procedures necessary to access CMOS/ECMOS information. +; +; Author: +; +; David Risner (o-ncrdr) 20 Apr 1992 +; +; Revision History: +; +; Landy Wang (corollary!landy) 04 Dec 1992 +; - Move much code from ixclock.asm to here so different HALs +; can reuse the common functionality. +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include mac386.inc +include i386\ix8259.inc +include i386\ixcmos.inc + .list + + EXTRNP _DbgBreakPoint,0,IMPORT + extrn _HalpSystemHardwareLock:DWORD + extrn _HalpBusType:DWORD + extrn _HalpSerialLen:BYTE + extrn _HalpSerialNumber:BYTE + + +CMOS_STATUS_BUSY EQU 80H ; Time update in progress +RTC_OFFSET_SECOND EQU 0 ; second field of RTC memory +RTC_OFFSET_MINUTE EQU 2 ; minute field of RTC memory +RTC_OFFSET_HOUR EQU 4 ; hour field of RTC memory +RTC_OFFSET_DAY_OF_WEEK EQU 6 ; day-of-week field of RTC memory +RTC_OFFSET_DATE_OF_MONTH EQU 7 ; date-of-month field of RTC memory +RTC_OFFSET_MONTH EQU 8 ; month field of RTC memory +RTC_OFFSET_YEAR EQU 9 ; year field of RTC memory +RTC_OFFSET_CENTURY_MCA EQU 37h ; Century field of RTC memory for MCA +RTC_OFFSET_CENTURY EQU 32h ; Century field of RTC memory +RTC_OFFSET_CENTURY_DS EQU 148h ; Bank 1, 48. Century field for DS +BANK1 EQU 100h + +; +; BCD_TO_BIN +; +; Description: Convert BCD value to binary +; +; Parameter: +; Input: (AL) = 2 digit BCD number to convert +; Output: (AX) = Binary equivalent (all in AL) +; +; Return: None. +; + +BCD_TO_BIN macro + + xor ah,ah + rol ax,4 + ror al,4 + aad +endm + +; +; BIN_TO_BCD +; +; Description: Convert binary value to BCD. +; +; Parameter: +; Input: (AL) = binary value to be converted. +; Output: (AX) = BCD (all in AL) +; +; Return: None. +; + +BIN_TO_BCD macro + + aam + rol al, 4 + ror ax, 4 +endm + + +_DATA SEGMENT DWORD PUBLIC 'DATA' + +; +; HalpRebootNow is a reboot vector. Set in an MP system, to +; cause any processors which may be looping in HalpAcquireCmosSinLock +; to transfer control to the vector in HalpRebootNow +; + + public _HalpRebootNow +_HalpRebootNow dd 0 + +; +; Holds the value of the eflags register before a cmos spinlock is +; acquired (used in HalpAcquire/ReleaseCmosSpinLock(). +; +_HalpHardwareLockFlags dd 0 + +; +; Holds the offset to CMOS Century information. +; + +_HalpCmosCenturyOffset dd 0 + +_DATA ends + + subttl "HalpGetCmosData" + +;++ +; +; CMOS space read and write functions. +; +;-- + +CmosAddressPort equ 70H +CmosDataPort equ 71H + +ECmosAddressLsbPort equ 74H +ECmosAddressMsbPort equ 75H +ECmosDataPort equ 76H + + +INIT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; HalpInitializeCmos( +; VOID +; ) +; +; This routine reads CMOS and initializes globals required for +; CMOS access, such as the location of the century byte. +; +;-- + +cPublicProc _HalpInitializeCmos,0 + + push ebx + push esi + push edi + +; +; Assume default +; + + mov eax, RTC_OFFSET_CENTURY + mov _HalpCmosCenturyOffset, eax + + cmp _HalpBusType, MACHINE_TYPE_ISA + jne short icm40 + + +; +; If control comes here, this is ISA machine. We need to check if this is +; IBM PS/1 or Pc/ValuePoint machine and use RTC_CENTURY_OFFSET_MCA to get +; Century byte from CMOS. +; + +; +; Check if the CMOS 2e and 2f contains memory checksum. On PS/1 machine +; the check should fail. +; + +icm20: mov ecx, 2dh ; from 10h to 2dh + mov eax, 0 ; clear ax + mov edx, 0 + +icm30: mov al, cl + CMOS_READ + add edx, eax + dec ecx + cmp ecx, 0fh + jne short icm30 + + mov eax, 2eh + CMOS_READ + mov ah, al + mov al, 2fh + CMOS_READ + cmp eax, edx + je short icm50 ; NOT PS/1 + + mov eax, RTC_OFFSET_CENTURY_MCA + mov _HalpCmosCenturyOffset, eax + jmp icm90 + +icm40: cmp _HalpBusType, MACHINE_TYPE_MCA + jne short icm50 + +; +; See if this is a P700 MCA machine +; + + in al, 07fh ; get PD700 ID byte + and al, 0F0h ; Mask high nibble + cmp al, 0A0h ; Is the ID Ax? + jz short icm50 + cmp al, 090h ; Or an 9X? + jz short icm50 ; Yes, it's a 700 + + mov eax, RTC_OFFSET_CENTURY_MCA + mov _HalpCmosCenturyOffset, eax + +icm50: + +if 0 + + - Selecting BANK1 causes some devices to mess up their month value + - For now, I'm removing this code until this problem can be solved + +; +; See if this is a Dallas Semiconductor DS17285 or later +; Switch to BANK 1 +; + mov al, 0Ah + CMOS_READ + + and al, 7fh ; Don't write UIP + mov ah, al + mov esi, eax ; save it for restore + or ah, 10h ; Set DV0 = 1 + + mov al, 0Ah ; Write register A + CMOS_WRITE + +; +; Check for RTC serial # with matching crc +; (al) = current byte +; (ah) = scratch register +; (bl) = current crc +; (bh) = zero, non-zero, flag +; (ecx) = cmos offset +; (edx) = used by cmos_read macro +; (esi) = saved register 0A +; + mov ecx, 40h + xor ebx, ebx + +icm60: mov al, cl + CMOS_READ + mov byte ptr _HalpSerialNumber+2+-40h[ecx], al + + or bh, al ; or to check for all zeros + + mov ch, 8 ; Bits per byte + +icm65: mov ah, bl ; ah = crc + xor ah, al ; xor LSb + shr bl, 1 ; shift crc + shr ah, 1 ; mov LSb to carry + sbb ah, ah ; if carry set 1's else 0's + and ah, (118h shr 1) ; crc polynomial + xor bl, ah ; apply it + + shr al, 1 ; next bit + dec ch ; + jnz short icm65 ; if ch non-zero, loop + + inc cl ; next cmos location + cmp cl, 48h ; at end? + jne short icm60 ; no, loop +; +; (bh) = zero, non-zero flag +; (bl) = crc +; + + mov eax, RTC_OFFSET_CENTURY_DS ; Read century byte + CMOS_READ + + BCD_TO_BIN + movzx ecx, ax ; save it + +; +; Switch back to BANK 0 +; + + mov eax, esi + mov al, 0Ah + CMOS_WRITE + +; +; Check for valid DS data +; + cmp bh, 0 ; Was data all zeros? + je short icm90 + + cmp bl, 0 ; was CRC valid? + jnz short icm90 + + cmp ecx, 19 ; Is century before 19? + jb short icm90 + + cmp ecx, 20 ; Is century after 20? + ja short icm90 + +; +; Setup for DS century byte +; + mov byte ptr _HalpSerialNumber+0, 'D' + mov byte ptr _HalpSerialNumber+1, 'S' + mov _HalpSerialLen, 10 + + mov eax, RTC_OFFSET_CENTURY_DS + mov _HalpCmosCenturyOffset, eax +endif + +icm90: pop edi + pop esi + pop ebx + stdRET _HalpInitializeCmos + +stdENDP _HalpInitializeCmos + + +INIT ends + +_TEXT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + +;++ +; +; ULONG +; HalpGetCmosData( +; IN ULONG SourceLocation +; IN ULONG SourceAddress +; IN ULONG ReturnBuffer +; IN PUCHAR ByteCount +; ) +; +; This routine reads the requested number of bytes from CMOS/ECMOS and +; stores the data read into the supplied buffer in system memory. If +; the requested data amount exceeds the allowable extent of the source +; location, the return data is truncated. +; +; Arguments: +; +; SourceLocation : where data is to be read from CMOS or ECMOS +; 0 - CMOS, 1 - ECMOS +; +; SourceAddress : address in CMOS/ECMOS where data is to be read from +; +; ReturnBuffer : address in system memory for return data +; +; ByteCount : number of bytes to be read +; +; Returns: +; +; Number of byte actually read. +; +;-- + +SourceLocation equ 2*4[ebp] +SourceAddress equ 3*4[ebp] +ReturnBuffer equ 4*4[ebp] +ByteCount equ 5*4[ebp] + +cPublicProc _HalpGetCmosData ,4 + + push ebp + mov ebp, esp + push ebx + push edi + + ; + ; NOTE: The spinlock is needed even in the UP case, because + ; the resource is also used in an interrupt handler (profiler). + ; If we own the spinlock in this routine, and we service + ; the profiler interrupt (which will wait for the spinlock forever), + ; then we have a hosed system. + ; + stdCall _HalpAcquireCmosSpinLock + + xor edx, edx ; initialize return data length + mov ecx, ByteCount + + or ecx, ecx ; validate requested byte count + jz HalpGetCmosDataExit ; if no work to do, exit + + mov edx, SourceAddress + mov edi, ReturnBuffer + + mov eax, SourceLocation ; cmos or extended cmos? + cmp eax, 1 + je ECmosReadByte + cmp eax, 0 + jne HalpGetCmosDataExit + +CmosReadByte: + cmp edx, 0ffH ; validate cmos source address + ja HalpGetCmosDataExit ; if out of range, exit + mov al, dl + out CmosAddressPort, al + in al, CmosDataPort + mov [edi], al + inc edx + inc edi + dec ecx + jnz CmosReadByte + jmp SHORT HalpGetCmosDataExit + +ECmosReadByte: + cmp edx,0ffffH ; validate ecmos source address + ja HalpGetCmosDataExit ; if out of range, exit + mov al, dl + out ECmosAddressLsbPort, al + mov al, dh + out ECmosAddressMsbPort, al + in al, ECmosDataPort + mov [edi], al + inc edx + inc edi + dec ecx + jnz ECmosReadByte + +HalpGetCmosDataExit: + stdCall _HalpReleaseCmosSpinLock + + mov eax, edx ; return bytes read + + pop edi + pop ebx + pop ebp + + stdRET _HalpGetCmosData + +stdENDP _HalpGetCmosData + + +;++ +; +; VOID +; HalpSetCmosData( +; IN ULONG SourceLocation +; IN ULONG SourceAddress +; IN ULONG ReturnBuffer +; IN PUCHAR ByteCount +; ) +; +; This routine writes the requested number of bytes to CMOS/ECMOS +; +; Arguments: +; +; SourceLocation : where data is to be written to CMOS or ECMOS +; 0 - CMOS, 1 - ECMOS +; +; SourceAddress : address in CMOS/ECMOS where data is to write to. +; +; ReturnBuffer : address in system memory for data to write +; +; ByteCount : number of bytes to be write +; +; Returns: +; +; Number of byte actually written. +; +;-- + +cPublicProc _HalpSetCmosData ,4 + + push ebp + mov ebp, esp + push ebx + push edi + + stdCall _HalpAcquireCmosSpinLock + + xor edx, edx ; initialize return data length + mov ecx, ByteCount + + or ecx, ecx ; validate requested byte count + jz HalpSetCmosDataExit ; if no work to do, exit + + mov edx, SourceAddress + mov edi, ReturnBuffer + + mov eax, SourceLocation ; cmos or extended cmos? + cmp eax, 1 + je ECmosWriteByte + cmp eax, 0 + jne HalpSetCmosDataExit + +CmosWriteByte: + cmp edx, 0ffH ; validate cmos source address + ja HalpSetCmosDataExit ; if out of range, exit + mov al, dl + out CmosAddressPort, al + mov al, [edi] + out CmosDataPort, al + inc edx + inc edi + dec ecx + jnz CmosWriteByte + jmp SHORT HalpSetCmosDataExit + +ECmosWriteByte: + cmp edx,0ffffH ; validate ecmos source address + ja HalpSetCmosDataExit ; if out of range, exit + mov al, dl + out ECmosAddressLsbPort, al + mov al, dh + out ECmosAddressMsbPort, al + mov al, [edi] + out ECmosDataPort, al + inc edx + inc edi + dec ecx + jnz ECmosWriteByte + +HalpSetCmosDataExit: + stdCall _HalpReleaseCmosSpinLock + + mov eax, edx ; return bytes written + pop edi + pop ebx + pop ebp + + stdRET _HalpSetCmosData + +stdENDP _HalpSetCmosData + + + page ,132 + subttl "Read System Time" +;++ +; +; VOID +; HalpReadCmosTime ( +; PTIME_FIELDS TimeFields +; ) +; +; Routine Description: +; +; This routine reads current time from CMOS memory and stores it +; in the TIME_FIELDS structure passed in by caller. +; +; Arguments: +; +; TimeFields - A pointer to the TIME_FIELDS structure. +; +; Return Value: +; +; None. +; +;-- + +; +; Parameters: +; + +KrctPTimeFields equ [esp+4] + +cPublicProc _HalpReadCmosTime ,1 + +if DBG +krctwait0: + mov ecx, 100 +krctwait: + push ecx +else +krctwait: +endif + stdCall _HalpAcquireCmosSpinLock + mov ecx, 100 + align 4 +krct00: mov al, 0Ah ; Specify register A + CMOS_READ ; (al) = CMOS register A + test al, CMOS_STATUS_BUSY ; Is time update in progress? + jz short krct10 ; if z, no, go read CMOS time + loop short krct00 ; otherwise, try again. + +; +; CMOS is still busy. Try again ... +; + + stdCall _HalpReleaseCmosSpinLock +if DBG + pop ecx + loop short krctwait + stdCall _DbgBreakPoint + jmp short krctwait0 +else + jmp short krctwait +endif + align 4 +if DBG +krct10: + pop ecx +else +krct10: +endif + mov edx, KrctPTimeFields ; (edx)-> TIME_FIELDS structure + xor eax, eax ; (eax) = 0 + + mov al, RTC_OFFSET_SECOND + CMOS_READ ; (al) = second in BCD form + BCD_TO_BIN ; (ax) = second + mov [edx].TfSecond, ax ; set second in TIME_FIELDS + + mov al, RTC_OFFSET_MINUTE + CMOS_READ ; (al) = minute in BCD form + BCD_TO_BIN ; (ax) = Minute + mov [edx].TfMinute, ax ; set minute in TIME_FIELDS + + mov al, RTC_OFFSET_HOUR + CMOS_READ ; (al) = hour in BCD form + BCD_TO_BIN ; (ax) = Hour + mov [edx].TfHour, ax ; set hour in TIME_FIELDS + + mov al, RTC_OFFSET_DAY_OF_WEEK + CMOS_READ ; (al) = day-of-week in BCD form + BCD_TO_BIN ; (ax) = day-of-week + mov [edx].TfWeekday, ax ; set Weekday in TIME_FIELDS + + mov al, RTC_OFFSET_DATE_OF_MONTH + CMOS_READ ; (al) = date-of-month in BCD form + BCD_TO_BIN ; (ax) = date_of_month + mov [edx].TfDay, ax ; set day in TIME_FIELDS + + mov al, RTC_OFFSET_MONTH + CMOS_READ ; (al) = month in BCD form + BCD_TO_BIN ; (ax) = month + mov [edx].TfMonth, ax ; set month in TIME_FIELDS + + mov al, RTC_OFFSET_YEAR + CMOS_READ ; (al) = year in BCD form + BCD_TO_BIN ; (ax) = year + push eax ; save year in stack + + push edx ; preserve edx + call _HalpGetCmosCenturyByte ; (al)= century byte in BCD form + BCD_TO_BIN ; (ax) = century + pop edx + + mov ah, 100 + mul ah ; (ax) = century * 100 + pop ecx ; (cx) = year + add ax, cx ; (ax)= year + + cmp ax, 1900 ; Is year > 1900 + jb short krct40 + cmp ax, 1920 ; and < 1920 + jae short krct40 + add ax, 100 ; Compensate for century field + +krct40: + mov [edx].TfYear, ax ; set year in TIME_FIELDS + + mov word ptr [edx].TfMilliseconds, 0 ; do not support + + stdCall _HalpReleaseCmosSpinLock + + stdRET _HalpReadCmosTime + +stdENDP _HalpReadCmosTime + + page ,132 + subttl "Write System Time" +;++ +; +; VOID +; HalpWriteCmosTime ( +; PTIME_FIELDS TimeFields +; ) +; +; Routine Description: +; +; This routine writes current time from TIME_FILEDS structure +; to CMOS memory. +; +; Arguments: +; +; TimeFields - A pointer to the TIME_FIELDS structure. +; +; Return Value: +; +; None. +; +;-- + +; +; Parameters: +; + +KrctPTimeFields equ [esp+4] + +cPublicProc _HalpWriteCmosTime ,1 + +if DBG +kwctwait0: + mov ecx, 100 +kwctwait: + push ecx +else +kwctwait: +endif + stdCall _HalpAcquireCmosSpinLock + mov ecx, 100 + align 4 +kwct00: mov al, 0Ah ; Specify register A + CMOS_READ ; (al) = CMOS register A + test al, CMOS_STATUS_BUSY ; Is time update in progress? + jz short kwct10 ; if z, no, go write CMOS time + loop short kwct00 ; otherwise, try again. + +; +; CMOS is still busy. Try again ... +; + + stdCall _HalpReleaseCmosSpinLock +if DBG + pop ecx + loop short kwctwait + stdCall _DbgBreakPoint + jmp short kwctwait0 +else + jmp short kwctwait +endif + align 4 +if DBG +kwct10: + pop ecx +else +kwct10: +endif + mov edx, KrctPTimeFields ; (edx)-> TIME_FIELDS structure + + mov al, [edx].TfSecond ; Read second in TIME_FIELDS + BIN_TO_BCD + mov ah, al + mov al, RTC_OFFSET_SECOND + CMOS_WRITE + + mov al, [edx].TfMinute ; Read minute in TIME_FIELDS + BIN_TO_BCD + mov ah, al + mov al, RTC_OFFSET_MINUTE + CMOS_WRITE + + mov al, [edx].TfHour ; Read Hour in TIME_FIELDS + BIN_TO_BCD + mov ah, al + mov al, RTC_OFFSET_HOUR + CMOS_WRITE + + mov al, [edx].TfWeekDay ; Read WeekDay in TIME_FIELDS + BIN_TO_BCD + mov ah, al + mov al, RTC_OFFSET_DAY_OF_WEEK + CMOS_WRITE + + mov al, [edx].TfDay ; Read day in TIME_FIELDS + BIN_TO_BCD + mov ah, al + mov al, RTC_OFFSET_DATE_OF_MONTH + CMOS_WRITE + + mov al, [edx].TfMonth ; Read month in TIME_FIELDS + BIN_TO_BCD + mov ah, al + mov al, RTC_OFFSET_MONTH + CMOS_WRITE + + mov ax, [edx].TfYear ; Read Year in TIME_FIELDS + cmp ax, 9999 + jbe short kwct15 + mov ax, 9999 + + align 4 +kwct15: + mov cl, 100 + div cl ; [ax]/[cl]->al=quo, ah=rem + push eax + BIN_TO_BCD + + push eax + call _HalpSetCmosCenturyByte + + pop eax + mov al, ah ; [al] = Year + BIN_TO_BCD + mov ah, al ; [ah] = year in BCD + mov al, RTC_OFFSET_YEAR + CMOS_WRITE + + stdCall _HalpReleaseCmosSpinLock + + stdRET _HalpWriteCmosTime + +stdENDP _HalpWriteCmosTime + + +;++ +; +; Routine Description: +; +; Acquires a spinlock to access the cmos chip. The cmos chip is +; accessed at different irql levels, so to be safe, we 'cli'. +; We could replace that to raise irql to PROFILE_LEVEL, but that's +; a lot of code. +; +; Arguments: +; +; None +; +; Return Value: +; +; Interrupt is disabled. +; Irql level not affected. +; Flags saved in _HalpHardwareLockFlags. +;-- + +cPublicProc _HalpAcquireCmosSpinLock ,0 + push eax + +Arsl10: pushfd + cli + lea eax, _HalpSystemHardwareLock + ACQUIRE_SPINLOCK eax, Arsl20 + pop _HalpHardwareLockFlags ; save flags for release S.L. + pop eax + stdRET _HalpAcquireCmosSpinLock + +Arsl20: popfd + +Arsl30: +ifndef NT_UP + cmp _HalpRebootNow, 0 + jnz short Arsl50 +endif + TEST_SPINLOCK eax, + jmp short ARsl10 + +Arsl50: +ifndef NT_UP + mov eax, _HalpRebootNow + call eax + int 3 ; should not return +endif + +stdENDP _HalpAcquireCmosSpinLock + + +;++ +; +; Routine Description: +; +; Release spinlock, and restore flags to the state it was before +; acquiring the spinlock. +; +; Arguments: +; +; None +; +; Return Value: +; +; Interrupts restored to their state before acquiring spinlock. +; Irql level not affected. +; +;-- + +cPublicProc _HalpReleaseCmosSpinLock ,0 + push eax + ; + ; restore eflags as it was before acquiring spinlock. Put it on + ; stack before releasing spinlock (so other cpus cannot overwrite + ; it with their own eflags). + ; + push _HalpHardwareLockFlags ; old eflags on stack. + lea eax, _HalpSystemHardwareLock + RELEASE_SPINLOCK eax + popfd ; restore eflags. + pop eax + stdRET _HalpReleaseCmosSpinLock +stdENDP _HalpReleaseCmosSpinLock + +;++ +; +; UCHAR +; HalpGetCmosCenturyByte ( +; VOID +; ) +; +; Routine Description: +; +; This routine gets Century byte from CMOS. +; +; Arguments: +; +; None +; +; Return Value: +; +; (al) = Century byte in BCD form. +; +;-- + +cPublicProc _HalpGetCmosCenturyByte, 0 + + mov eax, _HalpCmosCenturyOffset + +if DBG + +; +; Make sure the HalpCmosCenturyOffset is initialized +; + + cmp eax, 0 + jne short @f + + int 3 +@@: +endif + test eax, BANK1 + jnz short rcb50 + + CMOS_READ ; (al) = century in BCD form + stdRET _HalpGetCmosCenturyByte + +rcb50: mov edx, eax + + mov al, 0Ah + CMOS_READ + + mov dh, al ; save it for restore + or al, 10h ; Set DV0 = 1 + + mov ah, al + mov al, 0Ah ; Write register A + CMOS_WRITE + + mov al, dl ; century offset + CMOS_READ + mov dl, al ; save it + + mov ah, dh ; Restore DV0 + mov al, 0Ah ; Write register A + CMOS_WRITE + + mov al, dl + stdRET _HalpGetCmosCenturyByte + +stdENDP _HalpGetCmosCenturyByte + + +;++ +; +; VOID +; HalpSetCmosCenturyByte ( +; UCHAR Century +; ) +; +; Routine Description: +; +; This routine sets Century byte in CMOS. +; +; Arguments: +; +; Century - Supplies the value for CMOS century byte +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _HalpSetCmosCenturyByte, 1 + + mov eax, _HalpCmosCenturyOffset +if DBG + +; +; Make sure the HalpCmosCenturyOffset is initialized +; + + cmp eax, 0 + jne short @f + + int 3 +@@: +endif + + test eax, BANK1 + jnz short scb50 + + mov ah, [esp+4] ; (ah) = Century in BCD form + CMOS_WRITE + stdRET _HalpSetCmosCenturyByte + + +scb50: mov edx, eax + + mov al, 0Ah + CMOS_READ + + mov dh, al ; save it for restore + or al, 10h ; Set DV0 = 1 + + mov ah, al + mov al, 0Ah ; Write register A + CMOS_WRITE + + mov ah, [esp+4] ; (ah) = Century in BCD form + mov al, dl ; century offset + CMOS_WRITE + + mov ah, dh ; Restore DV0 + mov al, 0Ah ; Write register A + CMOS_WRITE + stdRET _HalpSetCmosCenturyByte + +stdENDP _HalpSetCmosCenturyByte + + +;++ +; +; VOID +; HalpCpuID ( +; ULONG InEax, +; PULONG OutEax, +; PULONG OutEbx, +; PULONG OutEcx, +; PULONG OutEdx +; ); +; +; Routine Description: +; +; Executes the CPUID instruction and returns the registers from it +; +; Only available at INIT time +; +; Arguments: +; +; Return Value: +; +;-- +cPublicProc _HalpCpuID,5 + + push ebx + push esi + + mov eax, [esp+12] + db 0fh, 0a2h ; CPUID + + mov esi, [esp+16] ; return EAX + mov [esi], eax + + mov esi, [esp+20] ; return EBX + mov [esi], ebx + + mov esi, [esp+24] ; return ECX + mov [esi], ecx + + mov esi, [esp+28] ; return EDX + mov [esi], edx + + pop esi + pop ebx + + stdRET _HalpCpuID + +stdENDP _HalpCpuID + + +;++ +; +; VOID +; HalpFlushTLB ( +; VOID +; ); +; +; Routine Description: +; +; Flush the current TLB. +; +; Arguments: +; +; Return Value: +; +;-- +cPublicProc _HalpFlushTLB, 0 +.586p + pushfd + push ebx + push esi + + cli + mov esi, cr3 + + mov ecx, PCR[PcPrcb] + cmp byte ptr [ecx].PbCpuID, 0 + jz short ftb50 + + mov eax, 1 ; Get feature bits + cpuid ; (note "cpuid" between CR3 reload fixes + ; P6 B step errata #11) + + test edx, 2000h ; see if 'G' bit is supported + jz short ftb50 + + mov ecx, cr4 ; 'G' bit is supported, due global flush + mov edx, ecx ; Save orginal cr4 + and ecx, not CR4_PGE ; Make sure global bit is disabled + mov cr4, ecx + mov cr3, esi ; flush TLB + mov cr4, edx ; restore cr4 + jmp short ftp99 + +ftb50: mov cr3, esi + +ftp99: pop esi + pop ebx + popfd + stdRET _HalpFlushTLB + +.486p +stdENDP _HalpFlushTLB + + +_TEXT ends + + end diff --git a/private/ntos/nthals/halx86/i386/ixcmos.inc b/private/ntos/nthals/halx86/i386/ixcmos.inc new file mode 100644 index 000000000..1f78de8e7 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixcmos.inc @@ -0,0 +1,66 @@ +;/* +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; ixcmos.inc +; +; Abstract: +; +; This module contains common definitions used by the CMOS. +; +; Author: +; +; Landy Wang (corollary!landy) 04-Dec-1992 +; +; (Moved from ixclock.asm) +; +;-- + +; +; _HalpAcquireCmosSpinLock and _HalpReleaseCmosSpinLock +; must be called before accessing the CMOS in both uniprocessor +; and multiprocessor systems. + +RTCIRQ EQU 8 ; IRQ number for RTC interrupt +CMOS_CONTROL_PORT EQU 70h ; command port for cmos +CMOS_DATA_PORT EQU 71h ; cmos data port + +; +; CMOS_READ +; +; Description: This macro reads a byte from the CMOS register specified +; in (AL). +; +; Parameter: (AL) = address/register to read +; Returns: (AL) = data +; + +CMOS_READ MACRO + OUT CMOS_CONTROL_PORT,al ; ADDRESS LOCATION AND DISABLE NMI + IODelay ; I/O DELAY + IN AL,CMOS_DATA_PORT ; READ IN REQUESTED CMOS DATA + IODelay ; I/O DELAY +ENDM + +; +; CMOS_WRITE +; +; Description: This macro reads a byte from the CMOS register specified +; in (AL). +; +; Parameter: (AL) = address/register to read +; (AH) = data to be written +; +; Return: None +; + +CMOS_WRITE MACRO + OUT CMOS_CONTROL_PORT,al ; ADDRESS LOCATION AND DISABLE NMI + IODelay ; I/O DELAY + MOV AL,AH ; (AL) = DATA + OUT CMOS_DATA_PORT,AL ; PLACE IN REQUESTED CMOS LOCATION + IODelay ; I/O DELAY +ENDM diff --git a/private/ntos/nthals/halx86/i386/ixdat.c b/private/ntos/nthals/halx86/i386/ixdat.c new file mode 100644 index 000000000..f6bc9cee3 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixdat.c @@ -0,0 +1,159 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ixdat.c + +Abstract: + + Declares various data which is initialize data, or pagable data. + +Author: + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "halp.h" + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg("INIT") +#endif + +// +// The following data is only valid during system initialiation +// and the memory will be re-claimed by the system afterwards +// + +ADDRESS_USAGE HalpDefaultPcIoSpace = { + NULL, CmResourceTypePort, InternalUsage, + { +#ifndef MCA + 0x000, 0x10, // ISA DMA + 0x0C0, 0x10, // ISA DMA +#else + 0x000, 0x20, // MCA DMA + 0x0C0, 0x20, // MCA DMA +#endif + 0x080, 0x10, // DMA + + 0x020, 0x2, // PIC + 0x0A0, 0x2, // Cascaded PIC + + 0x040, 0x4, // Timer1, Referesh, Speaker, Control Word + 0x048, 0x4, // Timer2, Failsafe + + 0x061, 0x1, // NMI (system control port B) + 0x092, 0x1, // system control port A + + 0x070, 0x2, // Cmos/NMI enable +#ifdef MCA + 0x074, 0x3, // Extended CMOS + + 0x090, 0x2, // Arbritration Control Port, Card Select Feedback + 0x093, 0x2, // Reserved, System board setup + 0x096, 0x2, // POS channel select +#endif + 0x0F0, 0x10, // coprocessor ports + 0,0 + } +}; + +ADDRESS_USAGE HalpEisaIoSpace = { + NULL, CmResourceTypePort, InternalUsage, + { + 0x0D0, 0x10, // DMA + 0x400, 0x10, // DMA + 0x480, 0x10, // DMA + 0x4C2, 0xE, // DMA + 0x4D4, 0x2C, // DMA + + 0x461, 0x2, // Extended NMI + 0x464, 0x2, // Last Eisa Bus Muster granted + + 0x4D0, 0x2, // edge/level control registers + + 0xC84, 0x1, // System board enable + 0, 0 + } +}; + +// +// Strings used for boot.ini options +// from mphal.c +// + +UCHAR HalpSzBreak[] = "BREAK"; +UCHAR HalpSzPciLock[] = "PCILOCK"; + +// +// From ixcmos.asm +// + +UCHAR HalpSerialLen; +UCHAR HalpSerialNumber[31]; + +// +// From usage.c +// + +WCHAR HalpSzSystem[] = L"\\Registry\\Machine\\Hardware\\Description\\System"; +WCHAR HalpSzSerialNumber[] = L"Serial Number"; + +ADDRESS_USAGE *HalpAddressUsageList; + +// +// Misc hal stuff in the registry +// + +WCHAR rgzHalClassName[] = L"Hardware Abstraction Layer"; + + +// +// From ixpcibus.c +// + +WCHAR rgzMultiFunctionAdapter[] = L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter"; +WCHAR rgzConfigurationData[] = L"Configuration Data"; +WCHAR rgzIdentifier[] = L"Identifier"; +WCHAR rgzPCIIndetifier[] = L"PCI"; + +// +// From ixpcibrd.c +// + +WCHAR rgzReservedResources[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\SystemResources\\ReservedResources"; + +// +// From ixinfo.c +// + +WCHAR rgzSuspendCallbackName[] = L"\\Callback\\SuspendHibernateSystem"; +UCHAR HalpGenuineIntel[]= "GenuineIntel"; + +// +// From ixmca.c +// +UCHAR MsgMCEPending[] = MSG_MCE_PENDING; +WCHAR rgzSessionManager[] = L"Session Manager"; +WCHAR rgzEnableMCE[] = L"EnableMCE"; +WCHAR rgzEnableMCA[] = L"EnableMCA"; + + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg() +#endif + +ULONG HalpFeatureBits; + +// +// IDT vector usage info +// + +IDTUsage HalpIDTUsage[MAXIMUM_IDTVECTOR+1]; diff --git a/private/ntos/nthals/halx86/i386/ixenvirv.c b/private/ntos/nthals/halx86/i386/ixenvirv.c new file mode 100644 index 000000000..62bdccf01 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixenvirv.c @@ -0,0 +1,183 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + ixenvirv.c + +Abstract: + + This module implements the HAL get and set environment variable routines + for a x86 system. + + Note that this particular implementation only supports the LastKnownGood + environment variable. This is done by using the Daylight Savings Time + bit in the Real Time Clock NVRAM. (Not pretty, but it's all we've got) + + Attempts to read or write any environment variable other than + LastKnownGood will fail. + +Author: + + John Vert (jvert) 22-Apr-1992 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "halp.h" +#include "arc.h" +#include "arccodes.h" +#include "string.h" + +#define CMOS_CONTROL_PORT ((PUCHAR)0x70) +#define CMOS_DATA_PORT ((PUCHAR)0x71) +#define CMOS_STATUS_B 0x0B +#define CMOS_DAYLIGHT_BIT 1 + + +ARC_STATUS +HalGetEnvironmentVariable ( + IN PCHAR Variable, + IN USHORT Length, + OUT PCHAR Buffer + ) + +/*++ + +Routine Description: + + This function locates an environment variable and returns its value. + + The only environment variable this implementation supports is + "LastKnownGood" It uses the Daylight Savings Time bit in the Real + TimeClock to indicate the state (TRUE/FALSE only) of this environment + variable. + +Arguments: + + Variable - Supplies a pointer to a zero terminated environment variable + name. + + Length - Supplies the length of the value buffer in bytes. + + Buffer - Supplies a pointer to a buffer that receives the variable value. + +Return Value: + + ESUCCESS is returned if the enviroment variable is located. Otherwise, + ENOENT is returned. + +--*/ + +{ + UCHAR StatusByte; + + UNREFERENCED_PARAMETER( Length ); + UNREFERENCED_PARAMETER( Buffer ); + + if (_stricmp(Variable, "LastKnownGood") != 0) { + return ENOENT; + } + + // + // Read the Daylight Savings Bit out of the RTC to determine whether + // the LastKnownGood environment variable is TRUE or FALSE. + // + + HalpAcquireCmosSpinLock(); + WRITE_PORT_UCHAR(CMOS_CONTROL_PORT, CMOS_STATUS_B); + StatusByte = READ_PORT_UCHAR(CMOS_DATA_PORT); + HalpReleaseCmosSpinLock (); + + if (StatusByte & CMOS_DAYLIGHT_BIT) { + strncpy(Buffer, "TRUE", Length); + } else { + strncpy(Buffer, "FALSE", Length); + } + + return ESUCCESS; +} + +ARC_STATUS +HalSetEnvironmentVariable ( + IN PCHAR Variable, + IN PCHAR Value + ) + +/*++ + +Routine Description: + + This function creates an environment variable with the specified value. + + The only environment variable this implementation supports is + "LastKnownGood" It uses the Daylight Savings Time bit in the Real + TimeClock to indicate the state (TRUE/FALSE only) of this environment + variable. + +Arguments: + + Variable - Supplies a pointer to an environment variable name. + + Value - Supplies a pointer to the environment variable value. + +Return Value: + + ESUCCESS is returned if the environment variable is created. Otherwise, + ENOMEM is returned. + +--*/ + +{ + UCHAR StatusByte; + + if (_stricmp(Variable, "LastKnownGood") != 0) { + return ENOMEM; + } + + if (_stricmp(Value, "TRUE") == 0) { + + HalpAcquireCmosSpinLock(); + // + // Turn Daylight Savings Bit on. + // + WRITE_PORT_UCHAR(CMOS_CONTROL_PORT, CMOS_STATUS_B); + StatusByte = READ_PORT_UCHAR(CMOS_DATA_PORT); + + StatusByte |= CMOS_DAYLIGHT_BIT; + + WRITE_PORT_UCHAR(CMOS_CONTROL_PORT, CMOS_STATUS_B); + WRITE_PORT_UCHAR(CMOS_DATA_PORT, StatusByte); + + HalpReleaseCmosSpinLock(); + + } else if (_stricmp(Value, "FALSE") == 0) { + + HalpAcquireCmosSpinLock(); + + // + // Turn Daylight Savings Bit off. + // + + WRITE_PORT_UCHAR(CMOS_CONTROL_PORT, CMOS_STATUS_B); + StatusByte = READ_PORT_UCHAR(CMOS_DATA_PORT); + + StatusByte &= ~CMOS_DAYLIGHT_BIT; + + WRITE_PORT_UCHAR(CMOS_CONTROL_PORT, CMOS_STATUS_B); + WRITE_PORT_UCHAR(CMOS_DATA_PORT, StatusByte); + + HalpReleaseCmosSpinLock(); + + } else { + return(ENOMEM); + } + + return ESUCCESS; +} diff --git a/private/ntos/nthals/halx86/i386/ixfirm.c b/private/ntos/nthals/halx86/i386/ixfirm.c new file mode 100644 index 000000000..1ebf1032a --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixfirm.c @@ -0,0 +1,79 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ixreboot.c + +Abstract: + + Provides the interface to the firmware for x86. Since there is no + firmware to speak of on x86, this is just reboot support. + +Author: + + John Vert (jvert) 12-Aug-1991 + +Revision History: + +--*/ +#include "halp.h" + +// +// Defines to let us diddle the CMOS clock and the keyboard +// + +#define CMOS_CTRL (PUCHAR )0x70 +#define CMOS_DATA (PUCHAR )0x71 + +#define RESET 0xfe +#define KEYBPORT (PUCHAR )0x64 + +VOID HalpVideoReboot(VOID); +VOID HalpReboot(VOID); + + +VOID +HalReturnToFirmware( + IN FIRMWARE_ENTRY Routine + ) + +/*++ + +Routine Description: + + Returns control to the firmware routine specified. Since the x86 has + no useful firmware, it just stops the system. + +Arguments: + + Routine - Supplies a value indicating which firmware routine to invoke. + +Return Value: + + Does not return. + +--*/ + +{ + switch (Routine) { + case HalHaltRoutine: + case HalPowerDownRoutine: + case HalRestartRoutine: + case HalRebootRoutine: + + HalpVideoReboot(); + + // + // Never returns + // + + HalpReboot(); + break; + default: + DbgPrint("HalReturnToFirmware called\n"); + DbgBreakPoint(); + break; + } +} diff --git a/private/ntos/nthals/halx86/i386/ixhwsup.c b/private/ntos/nthals/halx86/i386/ixhwsup.c new file mode 100644 index 000000000..b0f89592d --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixhwsup.c @@ -0,0 +1,758 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixhwsup.c + +Abstract: + + This module contains the IoXxx routines for the NT I/O system that + are hardware dependent. Were these routines not hardware dependent, + they would reside in the iosubs.c module. + +Author: + + Darryl E. Havens (darrylh) 11-Apr-1990 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "halp.h" + +#if MCA +#include "mca.h" +#else +#include "eisa.h" +#endif + + +PVOID HalpEisaControlBase; + +// +// Define save area for EISA adapter objects. +// + +PADAPTER_OBJECT HalpEisaAdapter[8]; + +VOID +HalpCopyBufferMap( + IN PMDL Mdl, + IN PTRANSLATION_ENTRY TranslationEntry, + IN PVOID CurrentVa, + IN ULONG Length, + IN BOOLEAN WriteToDevice + ); + + +VOID +HalpCopyBufferMap( + IN PMDL Mdl, + IN PTRANSLATION_ENTRY TranslationEntry, + IN PVOID CurrentVa, + IN ULONG Length, + IN BOOLEAN WriteToDevice + ) + +/*++ + +Routine Description: + + This routine copies the speicific data between the user's buffer and the + map register buffer. First a the user buffer is mapped if necessary, then + the data is copied. Finally the user buffer will be unmapped if + neccessary. + +Arguments: + + Mdl - Pointer to the MDL that describes the pages of memory that are + being read or written. + + TranslationEntry - The address of the base map register that has been + allocated to the device driver for use in mapping the transfer. + + CurrentVa - Current virtual address in the buffer described by the MDL + that the transfer is being done to or from. + + Length - The length of the transfer. This determines the number of map + registers that need to be written to map the transfer. + + WriteToDevice - Boolean value that indicates whether this is a write + to the device from memory (TRUE), or vice versa. + +Return Value: + + None. + +--*/ +{ + PCCHAR bufferAddress; + PCCHAR mapAddress; + + // + // Get the system address of the MDL. + // + + bufferAddress = MmGetSystemAddressForMdl(Mdl); + + // + // Calculate the actual start of the buffer based on the system VA and + // the current VA. + // + + bufferAddress += (PCCHAR) CurrentVa - (PCCHAR) MmGetMdlVirtualAddress(Mdl); + + mapAddress = (PCCHAR) TranslationEntry->VirtualAddress + + BYTE_OFFSET(CurrentVa); + + // + // Copy the data between the user buffer and map buffer + // + + if (WriteToDevice) { + + RtlMoveMemory( mapAddress, bufferAddress, Length); + + } else { + + RtlMoveMemory(bufferAddress, mapAddress, Length); + + } + +} + +PVOID +HalAllocateCommonBuffer( + IN PADAPTER_OBJECT AdapterObject, + IN ULONG Length, + OUT PPHYSICAL_ADDRESS LogicalAddress, + IN BOOLEAN CacheEnabled + ) +/*++ + +Routine Description: + + This function allocates the memory for a common buffer and maps so that it + can be accessed by a master device and the CPU. + +Arguments: + + AdapterObject - Supplies a pointer to the adapter object used by this + device. + + Length - Supplies the length of the common buffer to be allocated. + + LogicalAddress - Returns the logical address of the common buffer. + + CacheEnable - Indicates whether the memeory is cached or not. + +Return Value: + + Returns the virtual address of the common buffer. If the buffer cannot be + allocated then NULL is returned. + +--*/ + +{ + + PVOID virtualAddress; + PVOID virtualAddress2; + PHYSICAL_ADDRESS physicalAddress; + + UNREFERENCED_PARAMETER( CacheEnabled ); + + // + // Assume below 16M + // + + physicalAddress.HighPart = 0; + physicalAddress.LowPart = MAXIMUM_PHYSICAL_ADDRESS-1; + + // + // If the caller support 32bit addresses, and it's a master let + // it have any memory below 4G + // + + if (AdapterObject->Dma32BitAddresses && AdapterObject->MasterDevice) { + physicalAddress.LowPart = 0xFFFFFFFF; + } + + virtualAddress = MmAllocateContiguousMemory( + Length, + physicalAddress + ); + + if (virtualAddress == NULL) { + return(NULL); + } + + *LogicalAddress = MmGetPhysicalAddress(virtualAddress); + + if (HalpBusType != MACHINE_TYPE_ISA || AdapterObject->MasterDevice) { + return(virtualAddress); + } + + // + // This is an ISA system the common buffer cannot cross a 64 K bountry. + // + + if ((LogicalAddress->LowPart + Length & ~0xFFFF) == + (LogicalAddress->LowPart & ~0xFFFF)) { + + // + // This buffer is ok so return it. + // + + return(virtualAddress); + + } + + // + // Try to allocate a buffer agian and see if this is good. + // + + virtualAddress2 = MmAllocateContiguousMemory( + Length, + physicalAddress + ); + + // + // Free the first buffer. + // + + MmFreeContiguousMemory(virtualAddress); + + if (virtualAddress2 == NULL) { + return(NULL); + } + + *LogicalAddress = MmGetPhysicalAddress(virtualAddress2); + + if ((LogicalAddress->LowPart + Length & ~0xFFFF) == + (LogicalAddress->LowPart & ~0xFFFF)) { + + // + // This buffer is ok so return it. + // + + return(virtualAddress2); + + } + + // + // Try our best but just could not do it. Free the buffer. + // + + MmFreeContiguousMemory(virtualAddress2); + + return(NULL); +} + +BOOLEAN +HalFlushCommonBuffer( + IN PADAPTER_OBJECT AdapterObject, + IN ULONG Length, + IN PHYSICAL_ADDRESS LogicalAddress, + IN PVOID VirtualAddress + ) +/*++ + +Routine Description: + + This function is called to flush any hardware adapter buffers when the + driver needs to read data written by an I/O master device to a common + buffer. + +Arguments: + + AdapterObject - Supplies a pointer to the adapter object used by this + device. + + Length - Supplies the length of the common buffer. This should be the same + value used for the allocation of the buffer. + + LogicalAddress - Supplies the logical address of the common buffer. This + must be the same value return by HalAllocateCommonBuffer. + + VirtualAddress - Supplies the virtual address of the common buffer. This + must be the same value return by HalAllocateCommonBuffer. + +Return Value: + + Returns TRUE if no errors were detected; otherwise, FALSE is return. + +--*/ + +{ + UNREFERENCED_PARAMETER( AdapterObject ); + UNREFERENCED_PARAMETER( Length ); + UNREFERENCED_PARAMETER( LogicalAddress ); + UNREFERENCED_PARAMETER( VirtualAddress ); + + return(TRUE); + +} + +VOID +HalFreeCommonBuffer( + IN PADAPTER_OBJECT AdapterObject, + IN ULONG Length, + IN PHYSICAL_ADDRESS LogicalAddress, + IN PVOID VirtualAddress, + IN BOOLEAN CacheEnabled + ) +/*++ + +Routine Description: + + This function frees a common buffer and all of the resouces it uses. + +Arguments: + + AdapterObject - Supplies a pointer to the adapter object used by this + device. + + Length - Supplies the length of the common buffer. This should be the same + value used for the allocation of the buffer. + + LogicalAddress - Supplies the logical address of the common buffer. This + must be the same value return by HalAllocateCommonBuffer. + + VirtualAddress - Supplies the virtual address of the common buffer. This + must be the same value return by HalAllocateCommonBuffer. + + CacheEnable - Indicates whether the memeory is cached or not. + +Return Value: + + None + +--*/ + +{ + UNREFERENCED_PARAMETER( AdapterObject ); + UNREFERENCED_PARAMETER( Length ); + UNREFERENCED_PARAMETER( LogicalAddress ); + UNREFERENCED_PARAMETER( CacheEnabled ); + + MmFreeContiguousMemory (VirtualAddress); + +} + +VOID +IoFreeAdapterChannel( + IN PADAPTER_OBJECT AdapterObject + ) + +/*++ + +Routine Description: + + This routine is invoked to deallocate the specified adapter object. + Any map registers that were allocated are also automatically deallocated. + No checks are made to ensure that the adapter is really allocated to + a device object. However, if it is not, then kernel will bugcheck. + + If another device is waiting in the queue to allocate the adapter object + it will be pulled from the queue and its execution routine will be + invoked. + +Arguments: + + AdapterObject - Pointer to the adapter object to be deallocated. + +Return Value: + + None. + +--*/ + +{ + PKDEVICE_QUEUE_ENTRY Packet; + PWAIT_CONTEXT_BLOCK Wcb; + PADAPTER_OBJECT MasterAdapter; + BOOLEAN Busy = FALSE; + IO_ALLOCATION_ACTION Action; + KIRQL Irql; + LONG MapRegisterNumber; + + // + // Begin by getting the address of the master adapter. + // + + MasterAdapter = AdapterObject->MasterAdapter; + + // + // Pull requests of the adapter's device wait queue as long as the + // adapter is free and there are sufficient map registers available. + // + + while( TRUE ) { + + // + // Begin by checking to see whether there are any map registers that + // need to be deallocated. If so, then deallocate them now. + // + + if (AdapterObject->NumberOfMapRegisters != 0) { + IoFreeMapRegisters( AdapterObject, + AdapterObject->MapRegisterBase, + AdapterObject->NumberOfMapRegisters + ); + } + + // + // Simply remove the next entry from the adapter's device wait queue. + // If one was successfully removed, allocate any map registers that it + // requires and invoke its execution routine. + // + + Packet = KeRemoveDeviceQueue( &AdapterObject->ChannelWaitQueue ); + if (Packet == NULL) { + + // + // There are no more requests break out of the loop. + // + + break; + } + + Wcb = CONTAINING_RECORD( Packet, + WAIT_CONTEXT_BLOCK, + WaitQueueEntry ); + + AdapterObject->CurrentWcb = Wcb; + AdapterObject->NumberOfMapRegisters = Wcb->NumberOfMapRegisters; + + // + // Check to see whether this driver wishes to allocate any map + // registers. If so, then queue the device object to the master + // adapter queue to wait for them to become available. If the driver + // wants map registers, ensure that this adapter has enough total + // map registers to satisfy the request. + // + + if (Wcb->NumberOfMapRegisters != 0 && + AdapterObject->MasterAdapter != NULL) { + + // + // Lock the map register bit map and the adapter queue in the + // master adapter object. The channel structure offset is used as + // a hint for the register search. + // + + Irql = KfAcquireSpinLock( &MasterAdapter->SpinLock ); + + MapRegisterNumber = -1; + + if (IsListEmpty( &MasterAdapter->AdapterQueue)) { + MapRegisterNumber = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters, + Wcb->NumberOfMapRegisters, + 0 + ); + } + if (MapRegisterNumber == -1) { + + // + // There were not enough free map registers. Queue this request + // on the master adapter where is will wait until some registers + // are deallocated. + // + + InsertTailList( &MasterAdapter->AdapterQueue, + &AdapterObject->AdapterQueue + ); + Busy = 1; + + } else { + + AdapterObject->MapRegisterBase = ((PTRANSLATION_ENTRY) + MasterAdapter->MapRegisterBase + MapRegisterNumber); + + // + // Set the no scatter/gather flag if scatter/gather not + // supported. + // + + if (!AdapterObject->ScatterGather) { + + AdapterObject->MapRegisterBase = (PVOID) + ((ULONG) AdapterObject->MapRegisterBase | NO_SCATTER_GATHER); + + } + } + + KfReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + + } else { + + AdapterObject->MapRegisterBase = NULL; + AdapterObject->NumberOfMapRegisters = 0; + + } + + // + // If there were either enough map registers available or no map + // registers needed to be allocated, invoke the driver's execution + // routine now. + // + + if (!Busy) { + AdapterObject->CurrentWcb = Wcb; + Action = Wcb->DeviceRoutine( Wcb->DeviceObject, + Wcb->CurrentIrp, + AdapterObject->MapRegisterBase, + Wcb->DeviceContext ); + + // + // If the execution routine would like to have the adapter + // deallocated, then release the adapter object. + // + + if (Action == KeepObject) { + + // + // This request wants to keep the channel a while so break + // out of the loop. + // + + break; + + } + + // + // If the driver wants to keep the map registers then set the + // number allocated to 0. This keeps the deallocation routine + // from deallocating them. + // + + if (Action == DeallocateObjectKeepRegisters) { + AdapterObject->NumberOfMapRegisters = 0; + } + + } else { + + // + // This request did not get the requested number of map registers so + // out of the loop. + // + + break; + } + } +} + +VOID +IoFreeMapRegisters( + PADAPTER_OBJECT AdapterObject, + PVOID MapRegisterBase, + ULONG NumberOfMapRegisters + ) +/*++ + +Routine Description: + + This routine deallocates the map registers for the adapter. If there are + any queued adapter waiting for an attempt is made to allocate the next + entry. + +Arguments: + + AdapterObject - The adapter object to where the map register should be + returned. + + MapRegisterBase - The map register base of the registers to be deallocated. + + NumberOfMapRegisters - The number of registers to be deallocated. + +Return Value: + + None + +--+*/ +{ + PADAPTER_OBJECT MasterAdapter; + LONG MapRegisterNumber; + PWAIT_CONTEXT_BLOCK Wcb; + PLIST_ENTRY Packet; + IO_ALLOCATION_ACTION Action; + KIRQL Irql; + + + // + // Begin by getting the address of the master adapter. + // + + if (AdapterObject->MasterAdapter != NULL && MapRegisterBase != NULL) { + + MasterAdapter = AdapterObject->MasterAdapter; + + } else { + + // + // There are no map registers to return. + // + + return; + } + + // + // Strip no scatter/gather flag. + // + + MapRegisterBase = (PVOID) ((ULONG) MapRegisterBase & ~NO_SCATTER_GATHER); + + MapRegisterNumber = (PTRANSLATION_ENTRY) MapRegisterBase - + (PTRANSLATION_ENTRY) MasterAdapter->MapRegisterBase; + + // + // Acquire the master adapter spinlock which locks the adapter queue and the + // bit map for the map registers. + // + + Irql = KfAcquireSpinLock(&MasterAdapter->SpinLock); + + // + // Return the registers to the bit map. + // + + RtlClearBits( MasterAdapter->MapRegisters, + MapRegisterNumber, + NumberOfMapRegisters + ); + + // + // Process any requests waiting for map registers in the adapter queue. + // Requests are processed until a request cannot be satisfied or until + // there are no more requests in the queue. + // + + while(TRUE) { + + if ( IsListEmpty(&MasterAdapter->AdapterQueue) ){ + break; + } + + Packet = RemoveHeadList( &MasterAdapter->AdapterQueue ); + AdapterObject = CONTAINING_RECORD( Packet, + ADAPTER_OBJECT, + AdapterQueue + ); + Wcb = AdapterObject->CurrentWcb; + + // + // Attempt to allocate map registers for this request. Use the previous + // register base as a hint. + // + + MapRegisterNumber = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters, + AdapterObject->NumberOfMapRegisters, + MasterAdapter->NumberOfMapRegisters + ); + + if (MapRegisterNumber == -1) { + + // + // There were not enough free map registers. Put this request back on + // the adapter queue where is came from. + // + + InsertHeadList( &MasterAdapter->AdapterQueue, + &AdapterObject->AdapterQueue + ); + + break; + + } + + KfReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + + AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY) + MasterAdapter->MapRegisterBase + MapRegisterNumber); + + // + // Set the no scatter/gather flag if scatter/gather not + // supported. + // + + if (!AdapterObject->ScatterGather) { + + AdapterObject->MapRegisterBase = (PVOID) + ((ULONG) AdapterObject->MapRegisterBase | NO_SCATTER_GATHER); + + } + + // + // Invoke the driver's execution routine now. + // + + Action = Wcb->DeviceRoutine( Wcb->DeviceObject, + Wcb->CurrentIrp, + AdapterObject->MapRegisterBase, + Wcb->DeviceContext ); + + // + // If the driver wishes to keep the map registers then set the number + // allocated to zero and set the action to deallocate object. + // + + if (Action == DeallocateObjectKeepRegisters) { + AdapterObject->NumberOfMapRegisters = 0; + Action = DeallocateObject; + } + + // + // If the driver would like to have the adapter deallocated, + // then deallocate any map registers allocated and then release + // the adapter object. + // + + if (Action == DeallocateObject) { + + // + // The map registers registers are deallocated here rather than in + // IoFreeAdapterChannel. This limits the number of times + // this routine can be called recursively possibly overflowing + // the stack. The worst case occurs if there is a pending + // request for the adapter that uses map registers and whos + // excution routine decallocates the adapter. In that case if there + // are no requests in the master adapter queue, then IoFreeMapRegisters + // will get called again. + // + + if (AdapterObject->NumberOfMapRegisters != 0) { + + // + // Deallocate the map registers and clear the count so that + // IoFreeAdapterChannel will not deallocate them again. + // + + Irql = KfAcquireSpinLock( &MasterAdapter->SpinLock ); + + RtlClearBits( MasterAdapter->MapRegisters, + MapRegisterNumber, + AdapterObject->NumberOfMapRegisters + ); + + AdapterObject->NumberOfMapRegisters = 0; + + KfReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + } + + IoFreeAdapterChannel( AdapterObject ); + } + + Irql = KfAcquireSpinLock( &MasterAdapter->SpinLock ); + + } + + KfReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); +} diff --git a/private/ntos/nthals/halx86/i386/ixidle.asm b/private/ntos/nthals/halx86/i386/ixidle.asm new file mode 100644 index 000000000..8dea29cd9 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixidle.asm @@ -0,0 +1,88 @@ + title "Hal Processor Idle" +;++ +; +;Copyright (c) 1991 Microsoft Corporation +; +;Module Name: +; +; ixidle.asm +; +;Abstract: +; +; +;Author: +; +; +;Revision History: +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include mac386.inc + .list + +_TEXT$01 SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "HalProcessorIdle" +;++ +; +; VOID +; HalProcessorIdle( +; VOID +; ) +; +; Routine Description: +; +; This function is called when the current processor is idle. +; +; This function is called with interrupts disabled, and the processor +; is idle until it receives an interrupt. The does not need to return +; until an interrupt is received by the current processor. +; +; This is the lowest level of processor idle. It occurs frequently, +; and this function (alone) should not put the processor into a +; power savings mode which requeres large amount of time to enter & exit. +; +; Return Value: +; +;-- + +cPublicProc _HalProcessorIdle, 0 + + ; + ; the following code sequence "sti-halt" puts the processor + ; into a Halted state, with interrupts enabled, without processing + ; an interrupt before halting. The STI instruction has a delay + ; slot such that it does not take effect until after the instruction + ; following it - this has the effect of HALTing without allowing + ; a possible interrupt and then enabling interrupts while HALTed. + ; + + ; + ; On an MP hal we don't stop the processor, since that causes + ; the SNOOP to slow down as well + ; + + sti + +ifdef NT_UP + hlt +endif + + ; + ; Now return to the system. If there's still no work, then it + ; will call us back to halt again. + ; + + stdRET _HalProcessorIdle + +stdENDP _HalProcessorIdle + +_TEXT$01 ends + end diff --git a/private/ntos/nthals/halx86/i386/ixinfo.c b/private/ntos/nthals/halx86/i386/ixinfo.c new file mode 100644 index 000000000..d39fdcc35 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixinfo.c @@ -0,0 +1,285 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ixinfo.c + +Abstract: + +Author: + + Ken Reneris (kenr) 08-Aug-1994 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + + +#include "halp.h" + +#ifdef _PNP_POWER_ +HAL_CALLBACKS HalCallback; +extern WCHAR rgzSuspendCallbackName[]; + +VOID +HalInitSystemPhase2 ( + VOID + ); + +VOID +HalpLockSuspendCode ( + IN PVOID CallbackContext, + IN PVOID Argument1, + IN PVOID Argument2 + ); +#endif + +NTSTATUS +HalpQueryInstalledBusInformation ( + OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG ReturnedLength + ); + + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,HaliQuerySystemInformation) +#pragma alloc_text(PAGE,HaliSetSystemInformation) +#pragma alloc_text(INIT,HalInitSystemPhase2) + +#ifdef _PNP_POWER_ +#pragma alloc_text(PAGE,HalpLockSuspendCode) +#endif + +#endif + + +VOID +HalInitSystemPhase2 ( + VOID + ) +{ +#ifdef _PNP_POWER_ + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + UNICODE_STRING unicodeString; + PCALLBACK_OBJECT CallbackObject; + + // + // Create hal callbacks + // + + InitializeObjectAttributes( + &ObjectAttributes, + NULL, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + + ExCreateCallback (&HalCallback.SetSystemInformation, &ObjectAttributes, TRUE, TRUE); + ExCreateCallback (&HalCallback.BusCheck, &ObjectAttributes, TRUE, TRUE); + + // + // Connect to suspend callback to lock hal hibaration code + // + + RtlInitUnicodeString(&unicodeString, rgzSuspendCallbackName); + + InitializeObjectAttributes( + &ObjectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + Status = ExCreateCallback (&CallbackObject, &ObjectAttributes, FALSE, FALSE); + + if (NT_SUCCESS(Status)) { + ExRegisterCallback ( + CallbackObject, + HalpLockSuspendCode, + NULL + ); + + ObDereferenceObject (CallbackObject); + } +#endif +} + + +NTSTATUS +HaliQuerySystemInformation( + IN HAL_QUERY_INFORMATION_CLASS InformationClass, + IN ULONG BufferSize, + OUT PVOID Buffer, + OUT PULONG ReturnedLength + ) +{ + NTSTATUS Status; + PVOID InternalBuffer; + ULONG Length; + union { + HAL_POWER_INFORMATION PowerInf; + HAL_PROCESSOR_SPEED_INFORMATION ProcessorInf; + MCA_EXCEPTION McaException; + HAL_DISPLAY_BIOS_INFORMATION DisplayBiosInf; + } U; + + BOOLEAN bUseFrameBufferCaching; + + PAGED_CODE(); + + Status = STATUS_SUCCESS; + *ReturnedLength = 0; + Length = 0; + + switch (InformationClass) { + case HalInstalledBusInformation: + Status = HalpQueryInstalledBusInformation ( + Buffer, + BufferSize, + ReturnedLength + ); + break; + + case HalFrameBufferCachingInformation: + + // Note - we want to return TRUE here to enable USWC in all + // cases except in a "Shared Memory Cluster" machine. + bUseFrameBufferCaching = TRUE; + InternalBuffer = &bUseFrameBufferCaching; + Length = sizeof (BOOLEAN); + break; + + + case HalMcaLogInformation: + InternalBuffer = &U.McaException; + Status = HalpGetMcaLog (&U.McaException, &Length); + break; + + case HalDisplayBiosInformation: + InternalBuffer = &U.DisplayBiosInf; + Length = sizeof(U.DisplayBiosInf); + U.DisplayBiosInf = HalpGetDisplayBiosInformation (); + break; + +#ifdef _PNP_POWER_ + case HalPowerInformation: + RtlZeroMemory (&U.PowerInf, sizeof(HAL_POWER_INFORMATION)); + + InternalBuffer = &U.PowerInf; + Length = sizeof (HAL_POWER_INFORMATION); + break; + + + case HalProcessorSpeedInformation: + RtlZeroMemory (&U.ProcessorInf, sizeof(HAL_POWER_INFORMATION)); + + U.ProcessorInf.MaximumProcessorSpeed = 100; + U.ProcessorInf.CurrentAvailableSpeed = 100; + U.ProcessorInf.ConfiguredSpeedLimit = 100; + + InternalBuffer = &U.PowerInf; + Length = sizeof (HAL_PROCESSOR_SPEED_INFORMATION); + break; + + case HalCallbackInformation: + InternalBuffer = &HalCallback; + Length = sizeof (HAL_CALLBACKS); + break; +#endif + default: + Status = STATUS_INVALID_LEVEL; + break; + } + + // + // If non-zero Length copy data to callers buffer + // + + if (Length) { + if (BufferSize < Length) { + Length = BufferSize; + } + + *ReturnedLength = Length; + RtlCopyMemory (Buffer, InternalBuffer, Length); + } + + return Status; +} + +NTSTATUS +HaliSetSystemInformation ( + IN HAL_SET_INFORMATION_CLASS InformationClass, + IN ULONG BufferSize, + IN PVOID Buffer + ) +{ + NTSTATUS Status; + + PAGED_CODE(); + + Status = STATUS_SUCCESS; + + switch (InformationClass) { + + case HalMcaRegisterDriver: + Status = HalpMcaRegisterDriver( + (PMCA_DRIVER_INFO) Buffer // Info about registering driver + ); + break; + + default: + Status = STATUS_INVALID_LEVEL; + break; + } + + return Status; +} + + + +#ifdef _PNP_POWER_ + +VOID +HalpLockSuspendCode ( + IN PVOID CallbackContext, + IN PVOID Argument1, + IN PVOID Argument2 + ) +{ + static PVOID CodeLock; + + switch ((ULONG) Argument1) { + case 0: + // + // Lock code down which might be needed to perform a suspend + // + + ASSERT (CodeLock == NULL); + CodeLock = MmLockPagableCodeSection (&HaliSuspendHibernateSystem); + break; + + case 1: + // + // Release the code lock + // + + MmUnlockPagableImageSection (CodeLock); + CodeLock = NULL; + break; + } +} + +#endif diff --git a/private/ntos/nthals/halx86/i386/ixipi.asm b/private/ntos/nthals/halx86/i386/ixipi.asm new file mode 100644 index 000000000..3eb733e8c --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixipi.asm @@ -0,0 +1,188 @@ + title "Interprocessor Interrupt" +;++ +; +;Copyright (c) 1991 Microsoft Corporation +; +;Module Name: +; +; ixipi.asm +; +;Abstract: +; +; Provides the HAL support for Interprocessor Interrupts. +; This is the UP version. +; +;Author: +; +; John Vert (jvert) 16-Jul-1991 +; +;Revision History: +; +;-- +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include i386\ix8259.inc + + + EXTRNP _KiCoprocessorError,0,IMPORT + EXTRNP Kei386EoiHelper,0,IMPORT + EXTRNP _HalBeginSystemInterrupt,3 + EXTRNP _HalEndSystemInterrupt,2 + extrn _HalpDefaultInterruptAffinity:DWORD + extrn _HalpActiveProcessors:DWORD + + page ,132 + subttl "Post InterProcessor Interrupt" +INIT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; HalInitializeProcessor( +; ULONG Number +; ); +; +;Routine Description: +; +; Initialize hal pcr values for current processor (if any) +; (called shortly after processor reaches kernel, before +; HalInitSystem if P0) +; +; IPI's and KeReadir/LowerIrq's must be available once this function +; returns. (IPI's are only used once two or more processors are +; available) +; +;Arguments: +; +; Number - Logical processor number of calling processor +; +;Return Value: +; +; None. +; +;-- +cPublicProc _HalInitializeProcessor ,1 + +; +; Initialize PcIDR in PCR to enable slave IRQ +; + + mov fs:PcIDR, 0fffffffbh + mov dword ptr fs:PcStallScaleFactor, INITIAL_STALL_COUNT + + mov eax, dword ptr [esp+4] + lock bts _HalpDefaultInterruptAffinity, eax + lock bts _HalpActiveProcessors, eax + + stdRET _HalInitializeProcessor +stdENDP _HalInitializeProcessor + +INIT ends + + +_TEXT$03 SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; HalRequestIpi( +; IN ULONG Mask +; ); +; +;Routine Description: +; +; Requests an interprocessor interrupt +; +;Arguments: +; +; Mask - Supplies a mask of the processors to interrupt +; +;Return Value: +; +; None. +; +;-- + +cPublicProc _HalRequestIpi ,1 + +if DBG + int 3 +endif + stdRET _HalRequestIpi +stdENDP _HalRequestIpi + + + page ,132 + subttl "80387 Irq13 Interrupt Handler" +;++ +; +; VOID +; HalpIrq13Handler ( +; ); +; +; Routine Description: +; +; When the 80387 detects an error, it raises its error line. This +; was supposed to be routed directly to the 386 to cause a trap 16 +; (which would actually occur when the 386 encountered the next FP +; instruction). +; +; However, the ISA design routes the error line to IRQ13 on the +; slave 8259. So an interrupt will be generated whenever the 387 +; discovers an error. +; +; This routine handles that interrupt and passes control to the kernel +; coprocessor error handler. +; +; Arguments: +; +; None. +; Interrupt is disabled. +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST Hi13_a, Hi13_t + +cPublicProc _HalpIrq13Handler ,0 + +; +; Save machine state in trap frame +; + + ENTER_INTERRUPT Hi13_a, Hi13_t ; (ebp) -> Trap frame + +; +; Save previous IRQL +; + + push 13 + PRIMARY_VECTOR_BASE ; Vector + sub esp, 4 ; make space for OldIrql + + stdCall _HalBeginSystemInterrupt, + + stdCall _KiCoprocessorError ; call CoprocessorError handler + +; +; Clear the busy latch so that the 386 doesn't mistakenly think +; that the 387 is still busy. +; + + xor al,al + out I386_80387_BUSY_PORT, al + + INTERRUPT_EXIT ; will return to caller + +stdENDP _HalpIrq13Handler + +_TEXT$03 ENDS + + END diff --git a/private/ntos/nthals/halx86/i386/ixirql.asm b/private/ntos/nthals/halx86/i386/ixirql.asm new file mode 100644 index 000000000..e18a0ce8b --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixirql.asm @@ -0,0 +1,1297 @@ + title "Irql Processing" +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; ixirql.asm +; +; Abstract: +; +; This module implements the code necessary to raise and lower i386 +; Irql and dispatch software interrupts with the 8259 PIC. +; +; Author: +; +; Shie-Lin Tzong (shielint) 8-Jan-1990 +; +; Environment: +; +; Kernel mode only. +; +; Revision History: +; +; John Vert (jvert) 27-Nov-1991 +; Moved from kernel into HAL +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\ix8259.inc +include i386\kimacro.inc +include mac386.inc + .list + + + EXTRNP _KeBugCheck,1,IMPORT + EXTRNP _KiDispatchInterrupt,0,IMPORT + + extrn _HalpApcInterrupt:near + extrn _HalpDispatchInterrupt:near + extrn _KiUnexpectedInterrupt:near + extrn _HalpBusType:DWORD + extrn _HalpApcInterrupt2ndEntry:NEAR + extrn _HalpDispatchInterrupt2ndEntry:NEAR + extrn HalpSpecialDismissLevelTable:dword + extrn HalpSpecialDismissTable:dword + extrn _HalpEisaELCR:dword + +; +; Initialization control words equates for the PICs +; + +ICW1_ICW4_NEEDED equ 01H +ICW1_CASCADE equ 00H +ICW1_INTERVAL8 equ 00H +ICW1_LEVEL_TRIG equ 08H +ICW1_EDGE_TRIG equ 00H +ICW1_ICW equ 10H + +ICW4_8086_MODE equ 001H +ICW4_NORM_EOI equ 000H +ICW4_NON_BUF_MODE equ 000H +ICW4_SPEC_FULLY_NESTED equ 010H +ICW4_NOT_SPEC_FULLY_NESTED equ 000H + +OCW2_NON_SPECIFIC_EOI equ 020H +OCW2_SPECIFIC_EOI equ 060H +OCW2_SET_PRIORITY equ 0c0H + +PIC_SLAVE_IRQ equ 2 +PIC1_BASE equ 30H +PIC2_BASE equ 38H + +; +; Interrupt flag bit maks for EFLAGS +; + +EFLAGS_IF equ 200H +EFLAGS_SHIFT equ 9 + +; +; Hardware irq active masks +; + +IRQ_ACTIVE_MASK equ 0fffffff0h + +_DATA SEGMENT DWORD PUBLIC 'DATA' + +; +; PICsInitializationString - Master PIC initialization command string +; + +ifdef MCA + +PICsInitializationString dw PIC1_PORT0 + +; +; Master PIC initialization command +; + + db ICW1_ICW + ICW1_LEVEL_TRIG + ICW1_INTERVAL8 +\ + ICW1_CASCADE + ICW1_ICW4_NEEDED + db PIC1_BASE + db 1 SHL PIC_SLAVE_IRQ + db ICW4_NOT_SPEC_FULLY_NESTED + \ + ICW4_NON_BUF_MODE + \ + ICW4_NORM_EOI + \ + ICW4_8086_MODE +; +; Slave PIC initialization command strings +; + + dw PIC2_PORT0 + db ICW1_ICW + ICW1_LEVEL_TRIG + ICW1_INTERVAL8 +\ + ICW1_CASCADE + ICW1_ICW4_NEEDED + db PIC2_BASE + db PIC_SLAVE_IRQ + db ICW4_NOT_SPEC_FULLY_NESTED + \ + ICW4_NON_BUF_MODE + \ + ICW4_NORM_EOI + \ + ICW4_8086_MODE + dw 0 ; end of string + +else + +PICsInitializationString dw PIC1_PORT0 + +; +; Master PIC initialization command +; + + db ICW1_ICW + ICW1_EDGE_TRIG + ICW1_INTERVAL8 +\ + ICW1_CASCADE + ICW1_ICW4_NEEDED + db PIC1_BASE + db 1 SHL PIC_SLAVE_IRQ + db ICW4_NOT_SPEC_FULLY_NESTED + \ + ICW4_NON_BUF_MODE + \ + ICW4_NORM_EOI + \ + ICW4_8086_MODE +; +; Slave PIC initialization command strings +; + + dw PIC2_PORT0 + db ICW1_ICW + ICW1_EDGE_TRIG + ICW1_INTERVAL8 +\ + ICW1_CASCADE + ICW1_ICW4_NEEDED + db PIC2_BASE + db PIC_SLAVE_IRQ + db ICW4_NOT_SPEC_FULLY_NESTED + \ + ICW4_NON_BUF_MODE + \ + ICW4_NORM_EOI + \ + ICW4_8086_MODE + dw 0 ; end of string +endif + + align 4 + public KiI8259MaskTable +KiI8259MaskTable label dword + dd 00000000000000000000000000000000B ; irql 0 + dd 00000000000000000000000000000000B ; irql 1 + dd 00000000000000000000000000000000B ; irql 2 + dd 00000000000000000000000000000000B ; irql 3 + dd 11111111100000000000000000000000B ; irql 4 + dd 11111111110000000000000000000000B ; irql 5 + dd 11111111111000000000000000000000B ; irql 6 + dd 11111111111100000000000000000000B ; irql 7 + dd 11111111111110000000000000000000B ; irql 8 + dd 11111111111111000000000000000000B ; irql 9 + dd 11111111111111100000000000000000B ; irql 10 + dd 11111111111111110000000000000000B ; irql 11 + dd 11111111111111111000000000000000B ; irql 12 + dd 11111111111111111100000000000000B ; irql 13 + dd 11111111111111111110000000000000B ; irql 14 + dd 11111111111111111111000000000000B ; irql 15 + dd 11111111111111111111100000000000B ; irql 16 + dd 11111111111111111111110000000000B ; irql 17 + dd 11111111111111111111111000000000B ; irql 18 + dd 11111111111111111111111000000000B ; irql 19 + dd 11111111111111111111111010000000B ; irql 20 + dd 11111111111111111111111011000000B ; irql 21 + dd 11111111111111111111111011100000B ; irql 22 + dd 11111111111111111111111011110000B ; irql 23 + dd 11111111111111111111111011111000B ; irql 24 + dd 11111111111111111111111011111000B ; irql 25 + dd 11111111111111111111111011111010B ; irql 26 + dd 11111111111111111111111111111010B ; irql 27 + dd 11111111111111111111111111111011B ; irql 28 + dd 11111111111111111111111111111011B ; irql 29 + dd 11111111111111111111111111111011B ; irql 30 + dd 11111111111111111111111111111011B ; irql 31 + +; +; This table is used to mask all pending interrupts below a given Irql +; out of the IRR +; + align 4 + + public FindHigherIrqlMask +FindHigherIrqlMask label dword + dd 11111111111111111111111111111110B ; irql 0 + dd 11111111111111111111111111111100B ; irql 1 (APC) + dd 11111111111111111111111111111000B ; irql 2 (DISPATCH) + dd 11111111111111111111111111110000B ; irql 3 + dd 00000111111111111111111111110000B ; irql 4 + dd 00000011111111111111111111110000B ; irql 5 + dd 00000001111111111111111111110000B ; irql 6 + dd 00000000111111111111111111110000B ; irql 7 + dd 00000000011111111111111111110000B ; irql 8 + dd 00000000001111111111111111110000B ; irql 9 + dd 00000000000111111111111111110000B ; irql 10 + dd 00000000000011111111111111110000B ; irql 11 + dd 00000000000001111111111111110000B ; irql 12 + dd 00000000000000111111111111110000B ; irql 13 + dd 00000000000000011111111111110000B ; irql 14 + dd 00000000000000001111111111110000B ; irql 15 + dd 00000000000000000111111111110000B ; irql 16 + dd 00000000000000000011111111110000B ; irql 17 + dd 00000000000000000001111111110000B ; irql 18 + dd 00000000000000000001111111110000B ; irql 19 + dd 00000000000000000001011111110000B ; irql 20 + dd 00000000000000000001001111110000B ; irql 20 + dd 00000000000000000001000111110000B ; irql 22 + dd 00000000000000000001000011110000B ; irql 23 + dd 00000000000000000001000001110000B ; irql 24 + dd 00000000000000000001000000110000B ; irql 25 + dd 00000000000000000001000000010000B ; irql 26 + dd 00000000000000000000000000010000B ; irql 27 + dd 00000000000000000000000000000000B ; irql 28 + dd 00000000000000000000000000000000B ; irql 29 + dd 00000000000000000000000000000000B ; irql 30 + dd 00000000000000000000000000000000B ; irql 31 + + align 4 +; +; The following tables define the addresses of software interrupt routers +; + +; +; Use this table if there is NO machine state frame on stack already +; + + public SWInterruptHandlerTable +SWInterruptHandlerTable label dword + dd offset FLAT:_KiUnexpectedInterrupt ; irql 0 + dd offset FLAT:_HalpApcInterrupt ; irql 1 + dd offset FLAT:_HalpDispatchInterrupt2 ; irql 2 + dd offset FLAT:_KiUnexpectedInterrupt ; irql 3 + dd offset FLAT:HalpHardwareInterrupt00 ; 8259 irq#0 + dd offset FLAT:HalpHardwareInterrupt01 ; 8259 irq#1 + dd offset FLAT:HalpHardwareInterrupt02 ; 8259 irq#2 + dd offset FLAT:HalpHardwareInterrupt03 ; 8259 irq#3 + dd offset FLAT:HalpHardwareInterrupt04 ; 8259 irq#4 + dd offset FLAT:HalpHardwareInterrupt05 ; 8259 irq#5 + dd offset FLAT:HalpHardwareInterrupt06 ; 8259 irq#6 + dd offset FLAT:HalpHardwareInterrupt07 ; 8259 irq#7 + dd offset FLAT:HalpHardwareInterrupt08 ; 8259 irq#8 + dd offset FLAT:HalpHardwareInterrupt09 ; 8259 irq#9 + dd offset FLAT:HalpHardwareInterrupt10 ; 8259 irq#10 + dd offset FLAT:HalpHardwareInterrupt11 ; 8259 irq#11 + dd offset FLAT:HalpHardwareInterrupt12 ; 8259 irq#12 + dd offset FLAT:HalpHardwareInterrupt13 ; 8259 irq#13 + dd offset FLAT:HalpHardwareInterrupt14 ; 8259 irq#14 + dd offset FLAT:HalpHardwareInterrupt15 ; 8259 irq#15 + +; +; Use this table if there is already a machine state frame on stack +; + + public SWInterruptHandlerTable2 +SWInterruptHandlerTable2 label dword + dd offset FLAT:_KiUnexpectedInterrupt ; irql 0 + dd offset FLAT:_HalpApcInterrupt2ndEntry ; irql 1 + dd offset FLAT:_HalpDispatchInterrupt2ndEntry ; irql 2 + +; +; The following table picks up the highest pending software irq level +; from software irr +; + + public SWInterruptLookUpTable +SWInterruptLookUpTable label byte + db 0 ; SWIRR=0, so highest pending SW irql= 0 + db 0 ; SWIRR=1, so highest pending SW irql= 0 + db 1 ; SWIRR=2, so highest pending SW irql= 1 + db 1 ; SWIRR=3, so highest pending SW irql= 1 + db 2 ; SWIRR=4, so highest pending SW irql= 2 + db 2 ; SWIRR=5, so highest pending SW irql= 2 + db 2 ; SWIRR=6, so highest pending SW irql= 2 + db 2 ; SWIRR=7, so highest pending SW irql= 2 + +ifdef IRQL_METRICS + + public HalRaiseIrqlCount + public HalLowerIrqlCount + public HalQuickLowerIrqlCount + public HalApcSoftwareIntCount + public HalDpcSoftwareIntCount + public HalHardwareIntCount + public HalPostponedIntCount + public Hal8259MaskCount + +HalRaiseIrqlCount dd 0 +HalLowerIrqlCount dd 0 +HalQuickLowerIrqlCount dd 0 +HalApcSoftwareIntCount dd 0 +HalDpcSoftwareIntCount dd 0 +HalHardwareIntCount dd 0 +HalPostponedIntCount dd 0 +Hal8259MaskCount dd 0 + +endif +_DATA ENDS + + page ,132 + subttl "Raise Irql" + +_TEXT$01 SEGMENT PARA PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING +;++ +; +; KIRQL +; KfRaiseIrql ( +; IN KIRQL NewIrql, +; ) +; +; Routine Description: +; +; This routine is used to raise IRQL to the specified value. +; Also, a mask will be used to mask off all the lower lever 8259 +; interrupts. +; +; Arguments: +; +; (cl) = NewIrql - the new irql to be raised to +; +; Return Value: +; +; OldIrql - the addr of a variable which old irql should be stored +; +;-- + +cPublicFastCall KfRaiseIrql,1 +cPublicFpo 0, 0 + + xor eax, eax ; Eliminate partial stall on return to caller + mov al, PCR[PcIrql] ; (al) = Old Irql + mov PCR[PcIrql], cl ; set new irql + +ifdef IRQL_METRICS + inc HalRaiseIrqlCount +endif + +if DBG + cmp al, cl ; old > new? + ja short Kri99 ; yes, go bugcheck + + fstRET KfRaiseIrql + +cPublicFpo 2, 2 +Kri99: + movzx eax, al + movzx ecx, cl + push ecx ; put new irql where we can find it + push eax ; put old irql where we can find it + mov byte ptr PCR[PcIrql],0 ; avoid recursive error + stdCall _KeBugCheck, ; never return +endif + fstRET KfRaiseIrql + +fstENDP KfRaiseIrql + +;++ +; +; KIRQL +; KeRaiseIrqlToDpcLevel ( +; ) +; +; Routine Description: +; +; This routine is used to raise IRQL to DPC level. +; +; Arguments: +; +; Return Value: +; +; OldIrql - the addr of a variable which old irql should be stored +; +;-- + +cPublicProc _KeRaiseIrqlToDpcLevel,0 +cPublicFpo 0, 0 + + xor eax, eax ; Eliminate partial stall + mov al, PCR[PcIrql] ; (al) = Old Irql + mov byte ptr PCR[PcIrql], DISPATCH_LEVEL ; set new irql + +ifdef IRQL_METRICS + inc HalRaiseIrqlCount +endif +if DBG + cmp al, DISPATCH_LEVEL ; old > new? + ja short Krid99 ; yes, go bugcheck +endif + + stdRET _KeRaiseIrqlToDpcLevel + +if DBG +cPublicFpo 0,1 +Krid99: movzx eax, al + push eax ; put old irql where we can find it + stdCall _KeBugCheck, ; never return + stdRET _KeRaiseIrqlToDpcLevel +endif + +stdENDP _KeRaiseIrqlToDpcLevel + + +;++ +; +; KIRQL +; KeRaiseIrqlToSynchLevel ( +; ) +; +; Routine Description: +; +; This routine is used to raise IRQL to SYNC level. +; +; Arguments: +; +; Return Value: +; +; OldIrql - the addr of a variable which old irql should be stored +; +;-- + +cPublicProc _KeRaiseIrqlToSynchLevel,0 +cPublicFpo 0, 0 + + xor eax, eax ; Eliminate partial stall + mov al, PCR[PcIrql] ; (al) = Old Irql + mov byte ptr PCR[PcIrql], SYNCH_LEVEL ; set new irql + +ifdef IRQL_METRICS + inc HalRaiseIrqlCount +endif +if DBG + cmp al, SYNCH_LEVEL ; old > new? + ja short Kris99 ; yes, go bugcheck +endif + + stdRET _KeRaiseIrqlToSynchLevel + +if DBG +cPublicFpo 0,1 +Kris99: movzx eax, al + push eax ; put old irql where we can find it + stdCall _KeBugCheck, ; never return + stdRET _KeRaiseIrqlToSynchLevel +endif + +stdENDP _KeRaiseIrqlToSynchLevel + +;++ +; +; VOID +; KfLowerIrql ( +; IN KIRQL NewIrql +; ) +; +; Routine Description: +; +; This routine is used to lower IRQL to the specified value. +; The IRQL and PIRQL will be updated accordingly. Also, this +; routine checks to see if any software interrupt should be +; generated. The following condition will cause software +; interrupt to be simulated: +; any software interrupt which has higher priority than +; current IRQL's is pending. +; +; NOTE: This routine simulates software interrupt as long as +; any pending SW interrupt level is higher than the current +; IRQL, even when interrupts are disabled. +; +; Arguments: +; +; (cl) = NewIrql - the new irql to be set. +; +; Return Value: +; +; None. +; +;-- + +cPublicFastCall KfLowerIrql,1 +cPublicFpo 0, 0 + and ecx, 0ffh + +ifdef IRQL_METRICS + inc HalLowerIrqlCount +endif + +if DBG + cmp cl,PCR[PcIrql] ; Make sure we are not lowering to + ja KliBug ; ABOVE current level +endif + pushfd + cli + mov PCR[PcIrql], ecx + mov edx, PCR[PcIRR] + and edx, FindHigherIrqlMask[ecx*4] ; (edx) is the bitmask of + ; pending interrupts we need to + ; dispatch now. + jnz short Kli10 ; go dispatch pending interrupts + +; +; no interrupts pending, return quickly. +; + + popfd + +ifdef IRQL_METRICS + inc HalQuickLowerIrqlCount +endif + fstRET KfLowerIrql + +cPublicFpo 1, 1 +align 4 +Kli10: + +; +; If there is a pending hardware interrupt, then the PICs have been +; masked to reflect the actual Irql. +; + + bsr ecx, edx ; (ecx) = Pending irq level + cmp ecx, DISPATCH_LEVEL + ja short Kli40 + + call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int. + popfd + +cPublicFpo 1, 0 + fstRET KfLowerIrql + +Kli40: +; +; Clear all the interrupt masks +; + + mov eax, PCR[PcIDR] + SET_8259_MASK + + mov edx, 1 + shl edx, cl + xor PCR[PcIRR], edx ; clear bit in IRR + call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int. + popfd + +cPublicFpo 1, 0 + fstRET KfLowerIrql + +if DBG +cPublicFpo 1, 2 +KliBug: + push ecx ; new irql for debugging + push PCR[PcIrql] ; old irql for debugging + mov byte ptr PCR[PcIrql],HIGH_LEVEL ; avoid recursive error + stdCall _KeBugCheck, ; never return +endif + +fstENDP KfLowerIrql + +;++ +; +; VOID +; HalEndSystemInterrupt +; IN KIRQL NewIrql, +; IN ULONG Vector +; ) +; +; Routine Description: +; +; This routine is used to lower IRQL to the specified value. +; The IRQL and PIRQL will be updated accordingly. Also, this +; routine checks to see if any software interrupt should be +; generated. The following condition will cause software +; interrupt to be simulated: +; any software interrupt which has higher priority than +; current IRQL's is pending. +; +; NOTE: This routine simulates software interrupt as long as +; any pending SW interrupt level is higher than the current +; IRQL, even when interrupts are disabled. +; +; Arguments: +; +; NewIrql - the new irql to be set. +; +; Vector - Vector number of the interrupt +; +; Note that esp+12 is the beginning of interrupt/trap frame and upon +; entering to this routine the interrupts are off. +; +; Return Value: +; +; None. +; +;-- + +HeiNewIrql equ [esp + 4] + +cPublicProc _HalEndSystemInterrupt ,2 +cPublicFpo 2, 0 + + xor ecx, ecx + mov cl, byte ptr HeiNewIrql ; get new irql value + +ifdef IRQL_METRICS + inc HalLowerIrqlCount +endif + + mov edx, PCR[PcIRR] + and edx, FindHigherIrqlMask[ecx*4] ; (edx) is the bitmask of + ; pending interrupts we need to + ; dispatch now. + mov PCR[PcIrql], ecx + jnz short Hei10 ; go dispatch pending interrupts + +; +; no interrupts pending, return quickly. +; + + +ifdef IRQL_METRICS + inc HalQuickLowerIrqlCount +endif + stdRET _HalEndSystemInterrupt + +align 4 +Hei10: + +; +; If there is any delayed hardware interrupt being serviced, we leave +; the interrupt masked and simply return. +; + + test PCR[PcIrrActive], IRQ_ACTIVE_MASK + jnz short Hei50 + + bsr ecx, edx ; (eax) = Pending irq level + cmp ecx, DISPATCH_LEVEL + jle short Hei40 + +; +; Clear all the interrupt masks +; + +align 4 +Hei15: + mov eax, PCR[PcIDR] + SET_8259_MASK +; +; The pending interrupt is a hardware interrupt. To prevent the delayed +; interrupts from overflowing stack, we check if the pending level is already +; active. If yes, we simply return and let the higher level EndSystemInterrupt +; handle it. +; +; (ecx) = pending vector +; + + mov edx, 1 + shl edx, cl + test PCR[PcIrrActive], edx ; if the pending int is being + ; processed, just return. + jne short Hei50 + or PCR[PcIrrActive], edx ; Set Active bit + xor PCR[PcIRR], edx ; clear bit in IRR + call SWInterruptHandlerTable[ecx*4] ; Note, it destroys eax + xor PCR[PcIrrActive], edx ; Clear bit in ActiveIrql + mov eax, PCR[PcIRR] ; Reload IRR + mov ecx, PCR[PcIrql] + and eax, FindHigherIrqlMask[ecx*4] ; Is any interrupt pending + jz short Hei50 ; (Most time it will be zero.) + bsr ecx, eax ; (edx) = Pending irq level + cmp ecx, DISPATCH_LEVEL + ja short Hei15 + +Hei40: + +; +; The pending interrupt is at Software Level. We simply make current +; interrupt frame the new pending software interrupt's frame and +; jmp to the handler routine. +; + + add esp, 12 + jmp SWInterruptHandlerTable2[ecx*4] ; Note, it destroys eax + + +Hei50: + stdRET _HalEndSystemInterrupt + +stdENDP _HalEndSystemInterrupt + +;++ +; +; VOID +; HalpEndSoftwareInterrupt +; IN KIRQL NewIrql, +; ) +; +; Routine Description: +; +; This routine is used to lower IRQL from software interrupt +; leverl to the specified value. +; The IRQL and PIRQL will be updated accordingly. Also, this +; routine checks to see if any software interrupt should be +; generated. The following condition will cause software +; interrupt to be simulated: +; any software interrupt which has higher priority than +; current IRQL's is pending. +; +; NOTE: This routine simulates software interrupt as long as +; any pending SW interrupt level is higher than the current +; IRQL, even when interrupts are disabled. +; +; Arguments: +; +; NewIrql - the new irql to be set. +; +; Note that esp+8 is the beginning of interrupt/trap frame and upon +; entering to this routine the interrupts are off. +; +; Return Value: +; +; None. +; +;-- + +HesNewIrql equ [esp + 4] + +cPublicProc _HalpEndSoftwareInterrupt ,1 +cPublicFpo 1, 0 + + movzx ecx, byte ptr HesNewIrql ; get new irql value + mov edx, PCR[PcIRR] + and edx, FindHigherIrqlMask[ecx*4] ; (edx) is the bitmask of + ; pending interrupts we need to + ; dispatch now. + mov PCR[PcIrql], ecx + jnz short Hes10 + + stdRET _HalpEndSoftwareInterrupt + +align 4 +Hes10: +; +; Check if any delayed hardware interrupt is being serviced. If yes, we +; simply return. +; + + test PCR[PcIrrActive], IRQ_ACTIVE_MASK + jnz short Hes90 + +; +; If there is a pending hardware interrupt, then the PICs have been +; masked to reflect the actual Irql. +; + + bsr ecx, edx ; (ecx) = Pending irq level + cmp ecx, DISPATCH_LEVEL + ja short Hes20 + +; +; Pending interrupt is a soft interrupt. Recycle stack frame +; + + add esp, 8 + jmp SWInterruptHandlerTable2[ecx*4] ; Note, it destroys eax + +Hes20: +; +; Clear all the interrupt masks +; + + mov eax, PCR[PcIDR] + SET_8259_MASK + +; +; (ecx) = Pending level +; + + mov edx, 1 + shl edx, cl + + or PCR[PcIrrActive], edx ; Set Active bit + xor PCR[PcIRR], edx ; clear bit in IRR + + call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int. + + xor PCR[PcIrrActive], edx ; Clear bit in ActiveIrql + + movzx ecx, byte ptr HesNewIrql ; get new irql value + mov edx, PCR[PcIRR] + and edx, FindHigherIrqlMask[ecx*4] ; (edx) is the bitmask of + ; pending interrupts we need to + ; dispatch now. + jnz short Hes10 + +Hes90: stdRET _HalpEndSoftwareInterrupt + +stdENDP _HalpEndSoftwareInterrupt + + page ,132 + subttl "DispatchInterrupt 2" + +;++ +; +; VOID +; HalpDispatchInterrupt2( +; VOID +; ); +; +; Routine Description: +; +; The functional description is the same as HalpDispatchInterrupt. +; +; This function differs from HalpDispatchInterrupt in how it has been +; optimized. This function is optimized for dispatching dispatch interrupts +; for LowerIrql, ReleaseSpinLock, and RequestSoftwareInterrupt. +; +; Arguments: +; +; None +; Interrupt is disabled +; +; Return Value: +; +; (edx) = 1 shl DISPATCH_LEVEL +; +; Warnings: +; +; Not all SW int handles this hal uses save all the registers +; callers to SWInterruptHandler for H/W interrupts assume that +; ONLY EAX & ECX are destroyed. +; +; Note: this function saves EBX since KiDispatchInterrupt uses +; the value without preserving it. +;-- + +cPublicProc _HalpDispatchInterrupt2 +cPublicFpo 0, 2 + + xor ecx, ecx + and dword ptr PCR[PcIRR], not (1 shl DISPATCH_LEVEL) ; clear the pending bit in IRR + + mov cl, PCR[PcIrql] + + mov byte ptr PCR[PcIrql], DISPATCH_LEVEL; set new irql + push ecx ; Save OldIrql + +; +; Now it is safe to enable interrupt to allow higher priority interrupt +; to come in. +; + sti + + push ebx + stdCall _KiDispatchInterrupt ; Handle DispatchInterrupt + pop ebx + pop ecx ; (ecx) = OldIrql + mov edx, 1 shl DISPATCH_LEVEL + + cli + + mov eax, PCR[PcIRR] + mov PCR[PcIrql], ecx ; restore current irql + + and eax, FindHigherIrqlMask[ecx*4] ; (eax) is the bitmask of + ; pending interrupts we need to + ; dispatch now. + + jnz short diq10 ; go dispatch pending interrupts + stdRET _HalpDispatchInterrupt2 + +diq10: +; +; If there is a pending hardware interrupt, then the PICs have been +; masked to reflect the actual Irql. +; + + bsr ecx, eax ; (ecx) = Pending irq level + cmp ecx, DISPATCH_LEVEL + jbe short diq20 + +; +; Clear all the interrupt masks +; + + mov eax, PCR[PcIDR] + SET_8259_MASK + + mov edx, 1 + shl edx, cl + xor PCR[PcIRR], edx ; clear bit in IRR + +diq20: +; +; (ecx) = Pending level +; + + jmp SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int. +diq90: stdRET _HalpDispatchInterrupt2 + +stdENDP _HalpDispatchInterrupt2 + + page ,132 + subttl "Get current irql" + +;++ +; +; KIRQL +; KeGetCurrentIrql (VOID) +; +; Routine Description: +; +; This routine returns to current IRQL. +; +; Arguments: +; +; None. +; +; Return Value: +; +; The current IRQL. +; +;-- + +cPublicProc _KeGetCurrentIrql ,0 +cPublicFpo 0, 0 + + movzx eax, byte ptr PCR[PcIrql] ; Current irql is in the PCR + stdRET _KeGetCurrentIrql +stdENDP _KeGetCurrentIrql + + + +;++ +; +; VOID +; HalpDisableAllInterrupts (VOID) +; +; Routine Description: +; +; This routine is called during a system crash. The hal needs all +; interrupts disabled. +; +; Arguments: +; +; None. +; +; Return Value: +; +; None - all interrupts are masked off +; +;-- + +cPublicProc _HalpDisableAllInterrupts,0 +cPublicFpo 0, 0 + + ; + ; Mask interrupts off at PIC + ; (raising to high_level does not work on lazy irql implementation) + ; + mov eax, KiI8259MaskTable[HIGH_LEVEL*4]; get pic masks for the new irql + or eax, PCR[PcIDR] ; mask irqs which are disabled + SET_8259_MASK ; set 8259 masks + + mov byte ptr PCR[PcIrql], HIGH_LEVEL ; set new irql + + stdRET _HalpDisableAllInterrupts + +stdENDP _HalpDisableAllInterrupts + + page ,132 + subttl "Postponed Hardware Interrupt Dispatcher" +;++ +; +; VOID +; HalpHardwareInterruptNN ( +; VOID +; ); +; +; Routine Description: +; +; These routines branch through the IDT to simulate the appropriate +; hardware interrupt. They use the "INT nn" instruction to do this. +; +; Arguments: +; +; None. +; +; Returns: +; +; None. +; +; Environment: +; +; IRET frame is on the stack +; +;-- +cPublicProc _HalpHardwareInterruptTable, 0 +cPublicFpo 0,0 + + public HalpHardwareInterrupt00 +HalpHardwareInterrupt00 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 0 + ret + + public HalpHardwareInterrupt01 +HalpHardwareInterrupt01 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 1 + ret + + public HalpHardwareInterrupt02 +HalpHardwareInterrupt02 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 2 + ret + + public HalpHardwareInterrupt03 +HalpHardwareInterrupt03 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 3 + ret + + public HalpHardwareInterrupt04 +HalpHardwareInterrupt04 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 4 + ret + + public HalpHardwareInterrupt05 +HalpHardwareInterrupt05 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 5 + ret + + public HalpHardwareInterrupt06 +HalpHardwareInterrupt06 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 6 + ret + + public HalpHardwareInterrupt07 +HalpHardwareInterrupt07 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 7 + ret + + public HalpHardwareInterrupt08 +HalpHardwareInterrupt08 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 8 + ret + + public HalpHardwareInterrupt09 +HalpHardwareInterrupt09 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 9 + ret + + public HalpHardwareInterrupt10 +HalpHardwareInterrupt10 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 10 + ret + + public HalpHardwareInterrupt11 +HalpHardwareInterrupt11 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 11 + ret + + public HalpHardwareInterrupt12 +HalpHardwareInterrupt12 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 12 + ret + + public HalpHardwareInterrupt13 +HalpHardwareInterrupt13 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 13 + ret + + public HalpHardwareInterrupt14 +HalpHardwareInterrupt14 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 14 + ret + + public HalpHardwareInterrupt15 +HalpHardwareInterrupt15 label byte +ifdef IRQL_METRICS + lock inc HalHardwareIntCount +endif + int PRIMARY_VECTOR_BASE + 15 + ret + + public HalpHardwareInterruptLevel +HalpHardwareInterruptLevel label byte +cPublicFpo 0,0 + xor eax, eax + mov al, PCR[PcIrql] + mov ecx, PCR[PcIRR] + and ecx, FindHigherIrqlMask[eax*4] ; (ecx) is the bitmask of + ; pending interrupts we need to + ; dispatch now. + jz short lvl_90 ; no pending ints + + test PCR[PcIrrActive], IRQ_ACTIVE_MASK + jnz short lvl_90 ; let guy furture down the stack handle it + + mov eax, ecx ; (eax) = bitmask + bsr ecx, eax ; (cl) = set bit + + mov eax, 1 + shl eax, cl + xor PCR[PcIRR], eax ; clear bit in IRR + + call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int. +align 4 +lvl_90: + ret + +stdENDP _HalpHardwareInterruptTable + + +_TEXT$01 ends + + page ,132 + subttl "Interrupt Controller Chip Initialization" + +_TEXT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING +;++ +; +; VOID +; HalpInitializePICs ( +; ) +; +; Routine Description: +; +; This routine sends the 8259 PIC initialization commands and +; masks all the interrupts on 8259s. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- +cPublicProc _HalpInitializePICs ,0 +cPublicFpo 0, 0 + + push esi ; save caller's esi + cli ; disable interrupt + lea esi, PICsInitializationString + lodsw ; (AX) = PIC port 0 address +Hip10: movzx edx, ax + outsb ; output ICW1 + IODelay + inc edx ; (DX) = PIC port 1 address + outsb ; output ICW2 + IODelay + outsb ; output ICW3 + IODelay + outsb ; output ICW4 + IODelay + mov al, 0FFH ; mask all 8259 irqs + out dx,al ; write mask to PIC + lodsw + cmp ax, 0 ; end of init string? + jne short Hip10 ; go init next PIC + +; +; Read EISA defined ELCR. If it looks good save it away. +; If a PCI interrupts is later connected, the vector will +; be assumed level if it's in the ELCR. +; + mov edx, 4d1h ; Eisa Edge/Level port + in al, dx ; get e/l irq 8-f + mov ah, al + dec edx + in al, dx ; get e/l irq 0-7 + and eax, 0def8h ; clear reserved bits + cmp eax, 0def8h ; all set? + je short Hip50 ; Yes, register not implemented + + mov _HalpEisaELCR, eax ; Save possible ELCR settings + + +; +; If this is an EISA machine, mark all interrupts in the EISA ELCR +; as level interrupts +; + cmp _HalpBusType, MACHINE_TYPE_EISA + jne short Hip50 + +; +; Verify this isn't an OPTI chipset machine which claims to be +; EISA, but neglects to follow the EISA spec... +; + + mov edx, 0481h ; DmaPageHighPort.Channel2 + mov al, 055h + out dx, al ; out to Eisa DMA register + in al, dx ; read it back + cmp al, 055h ; if it doesn't stick, then machine + jne short Hip50 ; isn't support all eisa registers + +; +; Ok - loop and mark all EISA level interrupts +; + + mov eax, _HalpEisaELCR + xor ecx, ecx ; start at irq 0 +Hip30: + test eax, 1 ; is level bit set? + jz short Hip40 ; no, go to next + +; +; Found a level sensitive interrupt: +; Set the SWInterruptHandler for the irql to be a NOP. +; Set the SpecialDismiss entry for the irq to be the level version +; + mov SWInterruptHandlerTable+4*4[ecx], offset HalpHardwareInterruptLevel + + mov edx, HalpSpecialDismissLevelTable[ecx] + mov HalpSpecialDismissTable[ecx], edx + +Hip40: + add ecx, 4 ; next vector + shr eax, 1 ; shift bits down + jnz short Hip30 ; more set bits, then loop + + +Hip50: + pop esi ; restore caller's esi + sti ; enable interrupt + stdRET _HalpInitializePICs +stdENDP _HalpInitializePICs + + +_TEXT ends + + end diff --git a/private/ntos/nthals/halx86/i386/ixisa.h b/private/ntos/nthals/halx86/i386/ixisa.h new file mode 100644 index 000000000..1389962b9 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixisa.h @@ -0,0 +1,110 @@ +/*++ BUILD Version: 0001 // Increment this if a change has global effects + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ixisa.h + +Abstract: + + This header file defines the private Hardware Architecture Layer (HAL) + EISA/ISA specific interfaces, defines and structures. + +Author: + + Jeff Havens (jhavens) 20-Jun-91 + +Revision History: + +--*/ + +#ifndef _IXISA_ +#define _IXISA_ + + +// +// The MAXIMUM_MAP_BUFFER_SIZE defines the maximum map buffers which the system +// will allocate for devices which require phyically contigous buffers. +// + +#define MAXIMUM_MAP_BUFFER_SIZE 0x40000 + +// +// Define the initial buffer allocation size for a map buffers for systems with +// no memory which has a physical address greater than MAXIMUM_PHYSICAL_ADDRESS. +// + +#define INITIAL_MAP_BUFFER_SMALL_SIZE 0x10000 + +// +// Define the initial buffer allocation size for a map buffers for systems with +// no memory which has a physical address greater than MAXIMUM_PHYSICAL_ADDRESS. +// + +#define INITIAL_MAP_BUFFER_LARGE_SIZE 0x30000 + +// +// Define the incremental buffer allocation for a map buffers. +// + +#define INCREMENT_MAP_BUFFER_SIZE 0x10000 + +// +// Define the maximum number of map registers that can be requested at one time +// if actual map registers are required for the transfer. +// + +#define MAXIMUM_ISA_MAP_REGISTER 16 + +// +// Define the maximum physical address which can be handled by an Isa card. +// + +#define MAXIMUM_PHYSICAL_ADDRESS 0x01000000 + +// +// Define the scatter/gather flag for the Map Register Base. +// + +#define NO_SCATTER_GATHER 0x00000001 + +// +// Define the copy buffer flag for the index. +// + +#define COPY_BUFFER 0XFFFFFFFF + +// +// Define adapter object structure. +// + +typedef struct _ADAPTER_OBJECT { + CSHORT Type; + CSHORT Size; + struct _ADAPTER_OBJECT *MasterAdapter; + ULONG MapRegistersPerChannel; + PVOID AdapterBaseVa; + PVOID MapRegisterBase; + ULONG NumberOfMapRegisters; + ULONG CommittedMapRegisters; + struct _WAIT_CONTEXT_BLOCK *CurrentWcb; + KDEVICE_QUEUE ChannelWaitQueue; + PKDEVICE_QUEUE RegisterWaitQueue; + LIST_ENTRY AdapterQueue; + KSPIN_LOCK SpinLock; + PRTL_BITMAP MapRegisters; + PUCHAR PagePort; + UCHAR ChannelNumber; + UCHAR AdapterNumber; + USHORT DmaPortAddress; + UCHAR AdapterMode; + BOOLEAN NeedsMapRegisters; + BOOLEAN MasterDevice; + BOOLEAN Width16Bits; + BOOLEAN ScatterGather; + BOOLEAN IgnoreCount; + BOOLEAN Dma32BitAddresses; +} ADAPTER_OBJECT; + +#endif // _IXISA_ diff --git a/private/ntos/nthals/halx86/i386/ixisabus.c b/private/ntos/nthals/halx86/i386/ixisabus.c new file mode 100644 index 000000000..57d9348f1 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixisabus.c @@ -0,0 +1,640 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixisabus.c + +Abstract: + +Author: + +Environment: + +Revision History: + + +--*/ + +#include "halp.h" + +ULONG +HalpGetEisaInterruptVector( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +BOOLEAN +HalpTranslateIsaBusAddress ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ); + +BOOLEAN +HalpTranslateEisaBusAddress ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ); + +BOOLEAN +HalpTranslateSystemBusAddress ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ); + +NTSTATUS +HalpAdjustEisaResourceList ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList + ); + +HalpGetEisaData ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,HalpGetEisaInterruptVector) +#pragma alloc_text(PAGE,HalpAdjustEisaResourceList) +#pragma alloc_text(PAGE,HalpGetEisaData) +#endif + + +ULONG +HalpGetEisaInterruptVector( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ) + +/*++ + +Routine Description: + + This function returns the system interrupt vector and IRQL level + corresponding to the specified bus interrupt level and/or vector. The + system interrupt vector and IRQL are suitable for use in a subsequent call + to KeInitializeInterrupt. + +Arguments: + + BusHandle - Per bus specific structure + + Irql - Returns the system request priority. + + Affinity - Returns the system wide irq affinity. + +Return Value: + + Returns the system interrupt vector corresponding to the specified device. + +--*/ +{ + UNREFERENCED_PARAMETER( BusInterruptVector ); + + // + // On standard PCs, IRQ 2 is the cascaded interrupt, and it really shows + // up on IRQ 9. + // + if (BusInterruptLevel == 2) { + BusInterruptLevel = 9; + } + + if (BusInterruptLevel > 15) { + return 0; + } + + // + // Get parent's translation from here.. + // + return BusHandler->ParentHandler->GetInterruptVector ( + BusHandler->ParentHandler, + RootHandler, + BusInterruptLevel, + BusInterruptVector, + Irql, + Affinity + ); +} + +NTSTATUS +HalpAdjustEisaResourceList ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList + ) +{ + SUPPORTED_RANGE InterruptRange; + + RtlZeroMemory (&InterruptRange, sizeof InterruptRange); + InterruptRange.Base = 0; + InterruptRange.Limit = 15; + + return HaliAdjustResourceListRange ( + BusHandler->BusAddresses, + &InterruptRange, + pResourceList + ); +} + +BOOLEAN +HalpTranslateIsaBusAddress( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ) + +/*++ + +Routine Description: + + This function translates a bus-relative address space and address into + a system physical address. + +Arguments: + + BusAddress - Supplies the bus-relative address + + AddressSpace - Supplies the address space number. + Returns the host address space number. + + AddressSpace == 0 => memory space + AddressSpace == 1 => I/O space + + TranslatedAddress - Supplies a pointer to return the translated address + +Return Value: + + A return value of TRUE indicates that a system physical address + corresponding to the supplied bus relative address and bus address + number has been returned in TranslatedAddress. + + A return value of FALSE occurs if the translation for the address was + not possible + +--*/ + +{ + BOOLEAN Status; + + // + // Translated normally + // + + Status = HalpTranslateSystemBusAddress ( + BusHandler, + RootHandler, + BusAddress, + AddressSpace, + TranslatedAddress + ); + + + // + // If it could not be translated, and it's memory space + // then we allow the translation as it would occur on it's + // corrisponding EISA bus. We're allowing this because + // many VLBus drivers are claiming to be ISA devices. + // (yes, they should claim to be VLBus devices, but VLBus is + // run by video cards and like everything else about video + // there's no hope of fixing it. (At least according to + // Andre)) + // + + if (Status == FALSE && *AddressSpace == 0) { + Status = HalTranslateBusAddress ( + Eisa, + BusHandler->BusNumber, + BusAddress, + AddressSpace, + TranslatedAddress + ); + } + + return Status; +} + +BOOLEAN +HalpTranslateEisaBusAddress( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ) + +/*++ + +Routine Description: + + This function translates a bus-relative address space and address into + a system physical address. + +Arguments: + + BusAddress - Supplies the bus-relative address + + AddressSpace - Supplies the address space number. + Returns the host address space number. + + AddressSpace == 0 => memory space + AddressSpace == 1 => I/O space + + TranslatedAddress - Supplies a pointer to return the translated address + +Return Value: + + A return value of TRUE indicates that a system physical address + corresponding to the supplied bus relative address and bus address + number has been returned in TranslatedAddress. + + A return value of FALSE occurs if the translation for the address was + not possible + +--*/ + +{ + BOOLEAN Status; + + // + // Translated normally + // + + Status = HalpTranslateSystemBusAddress ( + BusHandler, + RootHandler, + BusAddress, + AddressSpace, + TranslatedAddress + ); + + + // + // If it could not be translated, and it's in the 640k - 1m + // range then (for compatibility) try translating it on the + // Internal bus for + // + + if (Status == FALSE && + *AddressSpace == 0 && + BusAddress.HighPart == 0 && + BusAddress.LowPart >= 0xA0000 && + BusAddress.LowPart < 0xFFFFF) { + + Status = HalTranslateBusAddress ( + Internal, + 0, + BusAddress, + AddressSpace, + TranslatedAddress + ); + } + + return Status; +} + + +HalpGetEisaData ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ) +/*++ + +Routine Description: + + The function returns the Eisa bus data for a slot or address. + +Arguments: + + Buffer - Supplies the space to store the data. + + Length - Supplies a count in bytes of the maximum amount to return. + +Return Value: + + Returns the amount of data stored into the buffer. + +--*/ + +{ + OBJECT_ATTRIBUTES ObjectAttributes; + OBJECT_ATTRIBUTES BusObjectAttributes; + PWSTR EisaPath = L"\\Registry\\Machine\\Hardware\\Description\\System\\EisaAdapter"; + PWSTR ConfigData = L"Configuration Data"; + ANSI_STRING TmpString; + ULONG BusNumber; + UCHAR BusString[] = "00"; + UNICODE_STRING RootName, BusName; + UNICODE_STRING ConfigDataName; + NTSTATUS NtStatus; + PKEY_VALUE_FULL_INFORMATION ValueInformation; + PCM_FULL_RESOURCE_DESCRIPTOR Descriptor; + PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResource; + PCM_EISA_SLOT_INFORMATION SlotInformation; + ULONG PartialCount; + ULONG TotalDataSize, SlotDataSize; + HANDLE EisaHandle, BusHandle; + ULONG BytesWritten, BytesNeeded; + PUCHAR KeyValueBuffer; + ULONG i; + ULONG DataLength = 0; + PUCHAR DataBuffer = Buffer; + BOOLEAN Found = FALSE; + + PAGED_CODE (); + + + RtlInitUnicodeString( + &RootName, + EisaPath + ); + + InitializeObjectAttributes( + &ObjectAttributes, + &RootName, + OBJ_CASE_INSENSITIVE, + (HANDLE)NULL, + NULL + ); + + // + // Open the EISA root + // + + NtStatus = ZwOpenKey( + &EisaHandle, + KEY_READ, + &ObjectAttributes + ); + + if (!NT_SUCCESS(NtStatus)) { + DbgPrint("HAL: Open Status = %x\n",NtStatus); + return(0); + } + + // + // Init bus number path + // + + BusNumber = BusHandler->BusNumber; + if (BusNumber > 99) { + return (0); + } + + if (BusNumber > 9) { + BusString[0] += (UCHAR) (BusNumber/10); + BusString[1] += (UCHAR) (BusNumber % 10); + } else { + BusString[0] += (UCHAR) BusNumber; + BusString[1] = '\0'; + } + + RtlInitAnsiString( + &TmpString, + BusString + ); + + RtlAnsiStringToUnicodeString( + &BusName, + &TmpString, + TRUE + ); + + + InitializeObjectAttributes( + &BusObjectAttributes, + &BusName, + OBJ_CASE_INSENSITIVE, + (HANDLE)EisaHandle, + NULL + ); + + // + // Open the EISA root + Bus Number + // + + NtStatus = ZwOpenKey( + &BusHandle, + KEY_READ, + &BusObjectAttributes + ); + + if (!NT_SUCCESS(NtStatus)) { + DbgPrint("HAL: Opening Bus Number: Status = %x\n",NtStatus); + return(0); + } + + // + // opening the configuration data. This first call tells us how + // much memory we need to allocate + // + + RtlInitUnicodeString( + &ConfigDataName, + ConfigData + ); + + // + // This should fail. We need to make this call so we can + // get the actual size of the buffer to allocate. + // + + ValueInformation = (PKEY_VALUE_FULL_INFORMATION) &i; + NtStatus = ZwQueryValueKey( + BusHandle, + &ConfigDataName, + KeyValueFullInformation, + ValueInformation, + 0, + &BytesNeeded + ); + + KeyValueBuffer = ExAllocatePool( + NonPagedPool, + BytesNeeded + ); + + if (KeyValueBuffer == NULL) { +#if DBG + DbgPrint("HAL: Cannot allocate Key Value Buffer\n"); +#endif + ZwClose(BusHandle); + return(0); + } + + ValueInformation = (PKEY_VALUE_FULL_INFORMATION)KeyValueBuffer; + + NtStatus = ZwQueryValueKey( + BusHandle, + &ConfigDataName, + KeyValueFullInformation, + ValueInformation, + BytesNeeded, + &BytesWritten + ); + + + ZwClose(BusHandle); + + if (!NT_SUCCESS(NtStatus)) { +#if DBG + DbgPrint("HAL: Query Config Data: Status = %x\n",NtStatus); +#endif + ExFreePool(KeyValueBuffer); + return(0); + } + + + // + // We get back a Full Resource Descriptor List + // + + Descriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PUCHAR)ValueInformation + + ValueInformation->DataOffset); + + PartialResource = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) + &(Descriptor->PartialResourceList.PartialDescriptors); + PartialCount = Descriptor->PartialResourceList.Count; + + for (i = 0; i < PartialCount; i++) { + + // + // Do each partial Resource + // + + switch (PartialResource->Type) { + case CmResourceTypeNull: + case CmResourceTypePort: + case CmResourceTypeInterrupt: + case CmResourceTypeMemory: + case CmResourceTypeDma: + + // + // We dont care about these. + // + + PartialResource++; + + break; + + case CmResourceTypeDeviceSpecific: + + // + // Bingo! + // + + TotalDataSize = PartialResource->u.DeviceSpecificData.DataSize; + + SlotInformation = (PCM_EISA_SLOT_INFORMATION) + ((PUCHAR)PartialResource + + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)); + + while (((LONG)TotalDataSize) > 0) { + + if (SlotInformation->ReturnCode == EISA_EMPTY_SLOT) { + + SlotDataSize = sizeof(CM_EISA_SLOT_INFORMATION); + + } else { + + SlotDataSize = sizeof(CM_EISA_SLOT_INFORMATION) + + SlotInformation->NumberFunctions * + sizeof(CM_EISA_FUNCTION_INFORMATION); + } + + if (SlotDataSize > TotalDataSize) { + + // + // Something is wrong again + // + + ExFreePool(KeyValueBuffer); + return(0); + + } + + if (SlotNumber != 0) { + + SlotNumber--; + + SlotInformation = (PCM_EISA_SLOT_INFORMATION) + ((PUCHAR)SlotInformation + SlotDataSize); + + TotalDataSize -= SlotDataSize; + + continue; + + } + + // + // This is our slot + // + + Found = TRUE; + break; + + } + + // + // End loop + // + + i = PartialCount; + + break; + + default: + +#if DBG + DbgPrint("Bad Data in registry!\n"); +#endif + + ExFreePool(KeyValueBuffer); + return(0); + + } + + } + + if (Found) { + i = Length + Offset; + if (i > SlotDataSize) { + i = SlotDataSize; + } + + DataLength = i - Offset; + RtlMoveMemory (Buffer, ((PUCHAR)SlotInformation + Offset), DataLength); + } + + ExFreePool(KeyValueBuffer); + return DataLength; +} diff --git a/private/ntos/nthals/halx86/i386/ixisasup.c b/private/ntos/nthals/halx86/i386/ixisasup.c new file mode 100644 index 000000000..cc4effc4f --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixisasup.c @@ -0,0 +1,1980 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixhwsup.c + +Abstract: + + This module contains the IoXxx routines for the NT I/O system that + are hardware dependent. Were these routines not hardware dependent, + they would reside in the iosubs.c module. + +Author: + + Darryl E. Havens (darrylh) 11-Apr-1990 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "halp.h" +#include "eisa.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,HalGetAdapter) +#endif + +// +// The HalpNewAdapter event is used to serialize allocations +// of new adapter objects, additions to the HalpEisaAdapter +// array, and some global values (MasterAdapterObject) and some +// adapter fields modified by HalpGrowMapBuffers. +// (AdapterObject->NumberOfMapRegisters is assumed not to be +// growable while this even is held) +// +// Note: We don't really need our own an event object for this. +// + +#define HalpNewAdapter HalpBusDatabaseEvent +extern KEVENT HalpNewAdapter; + +PVOID HalpEisaControlBase; +extern KSPIN_LOCK HalpSystemHardwareLock; + +// +// Define save area for EISA adapter objects. +// + +PADAPTER_OBJECT HalpEisaAdapter[8]; + +VOID +HalpCopyBufferMap( + IN PMDL Mdl, + IN PTRANSLATION_ENTRY TranslationEntry, + IN PVOID CurrentVa, + IN ULONG Length, + IN BOOLEAN WriteToDevice + ); + +PHYSICAL_ADDRESS +HalpMapTransfer( + IN PADAPTER_OBJECT AdapterObject, + IN PMDL Mdl, + IN PVOID MapRegisterBase, + IN PVOID CurrentVa, + IN OUT PULONG Length, + IN BOOLEAN WriteToDevice + ); + +VOID +HalpMapTransferHelper( + IN PMDL Mdl, + IN PVOID CurrentVa, + IN ULONG TransferLength, + IN PULONG PageFrame, + IN OUT PULONG Length + ); + + +NTSTATUS +HalAllocateAdapterChannel( + IN PADAPTER_OBJECT AdapterObject, + IN PWAIT_CONTEXT_BLOCK Wcb, + IN ULONG NumberOfMapRegisters, + IN PDRIVER_CONTROL ExecutionRoutine + ) +/*++ + +Routine Description: + + This routine allocates the adapter channel specified by the adapter object. + This is accomplished by placing the device object of the driver that wants + to allocate the adapter on the adapter's queue. If the queue is already + "busy", then the adapter has already been allocated, so the device object + is simply placed onto the queue and waits until the adapter becomes free. + + Once the adapter becomes free (or if it already is), then the driver's + execution routine is invoked. + + Also, a number of map registers may be allocated to the driver by specifying + a non-zero value for NumberOfMapRegisters. Then the map register must be + allocated from the master adapter. Once there are a sufficient number of + map registers available, then the execution routine is called and the + base address of the allocated map registers in the adapter is also passed + to the driver's execution routine. + +Arguments: + + AdapterObject - Pointer to the adapter control object to allocate to the + driver. + + Wcb - Supplies a wait context block for saving the allocation parameters. + The DeviceObject, CurrentIrp and DeviceContext should be initalized. + + NumberOfMapRegisters - The number of map registers that are to be allocated + from the channel, if any. + + ExecutionRoutine - The address of the driver's execution routine that is + invoked once the adapter channel (and possibly map registers) have been + allocated. + +Return Value: + + Returns STATUS_SUCESS unless too many map registers are requested. + +Notes: + + Note that this routine MUST be invoked at DISPATCH_LEVEL or above. + +--*/ +{ + + PADAPTER_OBJECT MasterAdapter; + BOOLEAN Busy = FALSE; + IO_ALLOCATION_ACTION Action; + KIRQL Irql; + ULONG MapRegisterNumber; + + // + // Begin by obtaining a pointer to the master adapter associated with this + // request. + // + + MasterAdapter = AdapterObject->MasterAdapter; + + // + // Initialize the device object's wait context block in case this device + // must wait before being able to allocate the adapter. + // + + Wcb->DeviceRoutine = ExecutionRoutine; + Wcb->NumberOfMapRegisters = NumberOfMapRegisters; + + // + // Allocate the adapter object for this particular device. If the + // adapter cannot be allocated because it has already been allocated + // to another device, then return to the caller now; otherwise, + // continue. + // + + if (!KeInsertDeviceQueue( &AdapterObject->ChannelWaitQueue, + &Wcb->WaitQueueEntry )) { + + // + // Save the parameters in case there are not enough map registers. + // + + AdapterObject->NumberOfMapRegisters = NumberOfMapRegisters; + AdapterObject->CurrentWcb = Wcb; + + // + // The adapter was not busy so it has been allocated. Now check + // to see whether this driver wishes to allocate any map registers. + // Ensure that this adapter has enough total map registers + // to satisfy the request. + // + + if (NumberOfMapRegisters != 0 && AdapterObject->NeedsMapRegisters) { + + // + // Lock the map register bit map and the adapter queue in the + // master adapter object. The channel structure offset is used as + // a hint for the register search. + // + + if (NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel) { + AdapterObject->NumberOfMapRegisters = 0; + IoFreeAdapterChannel(AdapterObject); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + Irql = KfAcquireSpinLock( &MasterAdapter->SpinLock ); + + MapRegisterNumber = (ULONG)-1; + + if (IsListEmpty( &MasterAdapter->AdapterQueue)) { + + MapRegisterNumber = RtlFindClearBitsAndSet( + MasterAdapter->MapRegisters, + NumberOfMapRegisters, + 0 + ); + } + + if (MapRegisterNumber == -1) { + + // + // There were not enough free map registers. Queue this request + // on the master adapter where is will wait until some registers + // are deallocated. + // + + InsertTailList( &MasterAdapter->AdapterQueue, + &AdapterObject->AdapterQueue + ); + Busy = 1; + + } else { + + // + // Calculate the map register base from the allocated map + // register and base of the master adapter object. + // + + AdapterObject->MapRegisterBase = ((PTRANSLATION_ENTRY) + MasterAdapter->MapRegisterBase + MapRegisterNumber); + + // + // Set the no scatter/gather flag if scatter/gather not + // supported. + // + + if (!AdapterObject->ScatterGather) { + + AdapterObject->MapRegisterBase = (PVOID) + ((ULONG) AdapterObject->MapRegisterBase | NO_SCATTER_GATHER); + + } + } + + KfReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + + } else { + + AdapterObject->MapRegisterBase = NULL; + AdapterObject->NumberOfMapRegisters = 0; + } + + // + // If there were either enough map registers available or no map + // registers needed to be allocated, invoke the driver's execution + // routine now. + // + + if (!Busy) { + + AdapterObject->CurrentWcb = Wcb; + Action = ExecutionRoutine( Wcb->DeviceObject, + Wcb->CurrentIrp, + AdapterObject->MapRegisterBase, + Wcb->DeviceContext ); + + // + // If the driver would like to have the adapter deallocated, + // then release the adapter object. + // + + if (Action == DeallocateObject) { + + IoFreeAdapterChannel( AdapterObject ); + + } else if (Action == DeallocateObjectKeepRegisters) { + + // + // Set the NumberOfMapRegisters = 0 in the adapter object. + // This will keep IoFreeAdapterChannel from freeing the + // registers. After this it is the driver's responsiblity to + // keep track of the number of map registers. + // + + AdapterObject->NumberOfMapRegisters = 0; + IoFreeAdapterChannel(AdapterObject); + + } + } + } + + return(STATUS_SUCCESS); + +} + +PVOID +HalAllocateCrashDumpRegisters( + IN PADAPTER_OBJECT AdapterObject, + IN PULONG NumberOfMapRegisters + ) +/*++ + +Routine Description: + + This routine is called during the crash dump disk driver's initialization + to allocate a number map registers permanently. + +Arguments: + + AdapterObject - Pointer to the adapter control object to allocate to the + driver. + NumberOfMapRegisters - Number of map registers requested. This field + will be updated to reflect the actual number of registers allocated + when the number is less than what was requested. + +Return Value: + + Returns STATUS_SUCESS if map registers allocated. + +--*/ +{ + PADAPTER_OBJECT MasterAdapter; + ULONG MapRegisterNumber; + + // + // Begin by obtaining a pointer to the master adapter associated with this + // request. + // + + MasterAdapter = AdapterObject->MasterAdapter; + + // + // Check to see whether this driver needs to allocate any map registers. + // + + if (AdapterObject->NeedsMapRegisters) { + + // + // Ensure that this adapter has enough total map registers to satisfy + // the request. + // + + if (*NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel) { + AdapterObject->NumberOfMapRegisters = 0; + return NULL; + } + + // + // Attempt to allocate the required number of map registers w/o + // affecting those registers that were allocated when the system + // crashed. + // + + MapRegisterNumber = (ULONG)-1; + + MapRegisterNumber = RtlFindClearBitsAndSet( + MasterAdapter->MapRegisters, + *NumberOfMapRegisters, + 0 + ); + + if (MapRegisterNumber == (ULONG)-1) { + + // + // Not enough free map registers were found, so they were busy + // being used by the system when it crashed. Force the appropriate + // number to be "allocated" at the base by simply overjamming the + // bits and return the base map register as the start. + // + + RtlSetBits( + MasterAdapter->MapRegisters, + 0, + *NumberOfMapRegisters + ); + MapRegisterNumber = 0; + + } + + // + // Calculate the map register base from the allocated map + // register and base of the master adapter object. + // + + AdapterObject->MapRegisterBase = ((PTRANSLATION_ENTRY) + MasterAdapter->MapRegisterBase + MapRegisterNumber); + + // + // Set the no scatter/gather flag if scatter/gather not + // supported. + // + + if (!AdapterObject->ScatterGather) { + + AdapterObject->MapRegisterBase = (PVOID) + ((ULONG) AdapterObject->MapRegisterBase | NO_SCATTER_GATHER); + + } + + } else { + + AdapterObject->MapRegisterBase = NULL; + AdapterObject->NumberOfMapRegisters = 0; + } + + return AdapterObject->MapRegisterBase; +} + +PADAPTER_OBJECT +HalGetAdapter( + IN PDEVICE_DESCRIPTION DeviceDescriptor, + OUT PULONG NumberOfMapRegisters + ) + +/*++ + +Routine Description: + + This function returns the appropriate adapter object for the device defined + in the device description structure. This code works for Isa and Eisa + systems. + +Arguments: + + DeviceDescriptor - Supplies a description of the deivce. + + NumberOfMapRegisters - Returns the maximum number of map registers which + may be allocated by the device driver. + +Return Value: + + A pointer to the requested adapter object or NULL if an adapter could not + be created. + +--*/ + +{ + PADAPTER_OBJECT adapterObject; + PVOID adapterBaseVa; + ULONG channelNumber; + ULONG controllerNumber; + DMA_EXTENDED_MODE extendedMode; + UCHAR adapterMode; + ULONG numberOfMapRegisters; + BOOLEAN useChannel; + ULONG maximumLength; + UCHAR DataByte; + + PAGED_CODE(); + + // + // Make sure this is the correct version. + // + + if (DeviceDescriptor->Version > DEVICE_DESCRIPTION_VERSION1) { + return( NULL ); + } + +#if DBG + if (DeviceDescriptor->Version == DEVICE_DESCRIPTION_VERSION1) { + ASSERT (DeviceDescriptor->Reserved1 == FALSE); + ASSERT (DeviceDescriptor->Reserved2 == FALSE); + } +#endif + + // + // Determine if the the channel number is important. Master cards on + // Eisa and Mca do not use a channel number. + // + + if (DeviceDescriptor->InterfaceType != Isa && + DeviceDescriptor->Master) { + + useChannel = FALSE; + } else { + + useChannel = TRUE; + } + + // + // Support for ISA local bus machines: + // If the driver is a Master but really does not want a channel since it + // is using the local bus DMA, just don't use an ISA channel. + // + + if (DeviceDescriptor->InterfaceType == Isa && + DeviceDescriptor->DmaChannel > 7) { + + useChannel = FALSE; + } + + // + // Determine if Eisa DMA is supported. + // + + if (HalpBusType == MACHINE_TYPE_EISA) { + + WRITE_PORT_UCHAR(&((PEISA_CONTROL) HalpEisaControlBase)->DmaPageHighPort.Channel2, 0x55); + DataByte = READ_PORT_UCHAR(&((PEISA_CONTROL) HalpEisaControlBase)->DmaPageHighPort.Channel2); + + if (DataByte == 0x55) { + HalpEisaDma = TRUE; + } + + } + + // + // Limit the maximum length to 2 GB this is done so that the BYTES_TO_PAGES + // macro works correctly. + // + + maximumLength = DeviceDescriptor->MaximumLength & 0x7fffffff; + + // + // Channel 4 cannot be used since it is used for chaining. Return null if + // it is requested. + // + + if (DeviceDescriptor->DmaChannel == 4 && useChannel) { + return(NULL); + } + + // + // Determine the number of map registers for this device. + // + + if (DeviceDescriptor->ScatterGather && + (LessThan16Mb || + DeviceDescriptor->InterfaceType == Eisa || + DeviceDescriptor->InterfaceType == PCIBus) ) { + + // + // Since the device support scatter/Gather then map registers are not + // required. + // + + numberOfMapRegisters = 0; + + } else { + + // + // Determine the number of map registers required based on the maximum + // transfer length, up to a maximum number. + // + + numberOfMapRegisters = BYTES_TO_PAGES(maximumLength) + + 1; + numberOfMapRegisters = numberOfMapRegisters > MAXIMUM_ISA_MAP_REGISTER ? + MAXIMUM_ISA_MAP_REGISTER : numberOfMapRegisters; + + // + // Make sure there where enough registers allocated initalize to support + // this size relaibly. This implies there must be to chunks equal to + // the allocatd size. This is only a problem on Isa systems where the + // map buffers cannot cross 64KB boundtires. + // + + if (!HalpEisaDma && + numberOfMapRegisters > HalpMapBufferSize / (PAGE_SIZE * 2)) { + + numberOfMapRegisters = (HalpMapBufferSize / (PAGE_SIZE * 2)); + } + + // + // If the device is not a master then it only needs one map register + // and does scatter/Gather. + // + + if (DeviceDescriptor->ScatterGather && !DeviceDescriptor->Master) { + + numberOfMapRegisters = 1; + } + } + + // + // Set the channel number number. + // + + channelNumber = DeviceDescriptor->DmaChannel & 0x03; + + // + // Set the adapter base address to the Base address register and controller + // number. + // + + if (!(DeviceDescriptor->DmaChannel & 0x04)) { + + controllerNumber = 1; + adapterBaseVa = (PVOID) &((PEISA_CONTROL) HalpEisaControlBase)->Dma1BasePort; + + } else { + + controllerNumber = 2; + adapterBaseVa = &((PEISA_CONTROL) HalpEisaControlBase)->Dma2BasePort; + + } + + // + // Determine if a new adapter object is necessary. If so then allocate it. + // + + if (useChannel && HalpEisaAdapter[DeviceDescriptor->DmaChannel] != NULL) { + + adapterObject = HalpEisaAdapter[DeviceDescriptor->DmaChannel]; + + if (adapterObject->NeedsMapRegisters) { + + if (numberOfMapRegisters > adapterObject->MapRegistersPerChannel) { + + adapterObject->MapRegistersPerChannel = numberOfMapRegisters; + } + } + + } else { + + // + // Serialize before allocating a new adapter + // + + KeWaitForSingleObject ( + &HalpNewAdapter, + WrExecutive, + KernelMode, + FALSE, + NULL + ); + + + // + // Determine if a new adapter object has already been allocated. + // If so use it, otherwise allocate a new adapter object + // + + if (useChannel && HalpEisaAdapter[DeviceDescriptor->DmaChannel] != NULL) { + + adapterObject = HalpEisaAdapter[DeviceDescriptor->DmaChannel]; + + if (adapterObject->NeedsMapRegisters) { + + if (numberOfMapRegisters > adapterObject->MapRegistersPerChannel) { + + adapterObject->MapRegistersPerChannel = numberOfMapRegisters; + } + } + + } else { + + // + // Allocate an adapter object. + // + + adapterObject = (PADAPTER_OBJECT) HalpAllocateAdapter( + numberOfMapRegisters, + adapterBaseVa, + NULL + ); + + if (adapterObject == NULL) { + KeSetEvent (&HalpNewAdapter, 0, FALSE); + return(NULL); + } + + if (useChannel) { + + HalpEisaAdapter[DeviceDescriptor->DmaChannel] = adapterObject; + + } + + // + // Set the maximum number of map registers for this channel bus on + // the number requested and the type of device. + // + + if (numberOfMapRegisters) { + + // + // The speicified number of registers are actually allowed to be + // allocated. + // + + adapterObject->MapRegistersPerChannel = numberOfMapRegisters; + + // + // Increase the commitment for the map registers. + // + + if (DeviceDescriptor->Master) { + + // + // Master I/O devices use several sets of map registers double + // their commitment. + // + + MasterAdapterObject->CommittedMapRegisters += + numberOfMapRegisters * 2; + + } else { + + MasterAdapterObject->CommittedMapRegisters += + numberOfMapRegisters; + + } + + // + // If the committed map registers is signicantly greater than the + // number allocated then grow the map buffer. + // + + if (MasterAdapterObject->CommittedMapRegisters > + MasterAdapterObject->NumberOfMapRegisters && + MasterAdapterObject->CommittedMapRegisters - + MasterAdapterObject->NumberOfMapRegisters > + MAXIMUM_ISA_MAP_REGISTER ) { + + HalpGrowMapBuffers( + MasterAdapterObject, + INCREMENT_MAP_BUFFER_SIZE + ); + } + + adapterObject->NeedsMapRegisters = TRUE; + + } else { + + // + // No real map registers were allocated. If this is a master + // device, then the device can have as may registers as it wants. + // + + adapterObject->NeedsMapRegisters = FALSE; + + if (DeviceDescriptor->Master) { + + adapterObject->MapRegistersPerChannel = BYTES_TO_PAGES( + maximumLength + ) + + 1; + + } else { + + // + // The device only gets one register. It must call + // IoMapTransfer repeatedly to do a large transfer. + // + + adapterObject->MapRegistersPerChannel = 1; + } + } + } + + KeSetEvent (&HalpNewAdapter, 0, FALSE); + + } + + adapterObject->IgnoreCount = FALSE; + if (DeviceDescriptor->Version >= DEVICE_DESCRIPTION_VERSION1) { + + // + // Move version 1 structure flags. + // IgnoreCount is used on machines where the DMA Counter + // is broken. (Namely PS/1 model 1000s). Setting this + // bit informs the hal not to rely on the DmaCount to determine + // how much data was DMAed. + // + + adapterObject->IgnoreCount = DeviceDescriptor->IgnoreCount; + } + + adapterObject->Dma32BitAddresses = DeviceDescriptor->Dma32BitAddresses; + adapterObject->ScatterGather = DeviceDescriptor->ScatterGather; + *NumberOfMapRegisters = adapterObject->MapRegistersPerChannel; + + if (DeviceDescriptor->Master) { + + adapterObject->MasterDevice = TRUE; + + } else { + + adapterObject->MasterDevice = FALSE; + + } + + // + // If the channel number is not used then we are finished. The rest of + // the work deals with channels. + // + + if (!useChannel) { + return(adapterObject); + } + + // + // Setup the pointers to all the random registers. + // + + adapterObject->ChannelNumber = (UCHAR) channelNumber; + + if (controllerNumber == 1) { + + switch ((UCHAR)channelNumber) { + + case 0: + adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel0; + break; + + case 1: + adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel1; + break; + + case 2: + adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel2; + break; + + case 3: + adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel3; + break; + } + + // + // Set the adapter number. + // + + adapterObject->AdapterNumber = 1; + + // + // Save the extended mode register address. + // + + adapterBaseVa = + &((PEISA_CONTROL) HalpEisaControlBase)->Dma1ExtendedModePort; + + } else { + + switch (channelNumber) { + case 1: + adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel5; + break; + + case 2: + adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel6; + break; + + case 3: + adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel7; + break; + } + + // + // Set the adapter number. + // + + adapterObject->AdapterNumber = 2; + + // + // Save the extended mode register address. + // + adapterBaseVa = + &((PEISA_CONTROL) HalpEisaControlBase)->Dma2ExtendedModePort; + + } + + + adapterObject->Width16Bits = FALSE; + + if (HalpEisaDma) { + + // + // Initialzie the extended mode port. + // + + *((PUCHAR) &extendedMode) = 0; + extendedMode.ChannelNumber = (UCHAR)channelNumber; + + switch (DeviceDescriptor->DmaSpeed) { + case Compatible: + extendedMode.TimingMode = COMPATIBLITY_TIMING; + break; + + case TypeA: + extendedMode.TimingMode = TYPE_A_TIMING; + break; + + case TypeB: + extendedMode.TimingMode = TYPE_B_TIMING; + break; + + case TypeC: + extendedMode.TimingMode = BURST_TIMING; + break; + + default: + ObDereferenceObject( adapterObject ); + return(NULL); + + } + + switch (DeviceDescriptor->DmaWidth) { + case Width8Bits: + extendedMode.TransferSize = BY_BYTE_8_BITS; + break; + + case Width16Bits: + extendedMode.TransferSize = BY_BYTE_16_BITS; + + // + // Note Width16bits should not be set here because there is no need + // to shift the address and the transfer count. + // + + break; + + case Width32Bits: + extendedMode.TransferSize = BY_BYTE_32_BITS; + break; + + default: + ObDereferenceObject( adapterObject ); + return(NULL); + + } + + WRITE_PORT_UCHAR( adapterBaseVa, *((PUCHAR) &extendedMode)); + + } else if (!DeviceDescriptor->Master) { + + + switch (DeviceDescriptor->DmaWidth) { + case Width8Bits: + + // + // The channel must use controller 1. + // + + if (controllerNumber != 1) { + ObDereferenceObject( adapterObject ); + return(NULL); + } + + break; + + case Width16Bits: + + // + // The channel must use controller 2. + // + + if (controllerNumber != 2) { + ObDereferenceObject( adapterObject ); + return(NULL); + } + + adapterObject->Width16Bits = TRUE; + break; + + default: + ObDereferenceObject( adapterObject ); + return(NULL); + + } + } + + // + // Initialize the adapter mode register value to the correct parameters, + // and save them in the adapter object. + // + + adapterMode = 0; + ((PDMA_EISA_MODE) &adapterMode)->Channel = adapterObject->ChannelNumber; + + if (DeviceDescriptor->Master) { + + ((PDMA_EISA_MODE) &adapterMode)->RequestMode = CASCADE_REQUEST_MODE; + + // + // Set the mode, and enable the request. + // + + if (adapterObject->AdapterNumber == 1) { + + // + // This request is for DMA controller 1 + // + + PDMA1_CONTROL dmaControl; + + dmaControl = adapterObject->AdapterBaseVa; + + WRITE_PORT_UCHAR( &dmaControl->Mode, adapterMode ); + + // + // Unmask the DMA channel. + // + + WRITE_PORT_UCHAR( + &dmaControl->SingleMask, + (UCHAR) (DMA_CLEARMASK | adapterObject->ChannelNumber) + ); + + } else { + + // + // This request is for DMA controller 1 + // + + PDMA2_CONTROL dmaControl; + + dmaControl = adapterObject->AdapterBaseVa; + + WRITE_PORT_UCHAR( &dmaControl->Mode, adapterMode ); + + // + // Unmask the DMA channel. + // + + WRITE_PORT_UCHAR( + &dmaControl->SingleMask, + (UCHAR) (DMA_CLEARMASK | adapterObject->ChannelNumber) + ); + + } + + } else if (DeviceDescriptor->DemandMode) { + + ((PDMA_EISA_MODE) &adapterMode)->RequestMode = DEMAND_REQUEST_MODE; + + } else { + + ((PDMA_EISA_MODE) &adapterMode)->RequestMode = SINGLE_REQUEST_MODE; + + } + + if (DeviceDescriptor->AutoInitialize) { + + ((PDMA_EISA_MODE) &adapterMode)->AutoInitialize = 1; + + } + + adapterObject->AdapterMode = adapterMode; + + return(adapterObject); +} + + +PHYSICAL_ADDRESS +IoMapTransfer( + IN PADAPTER_OBJECT AdapterObject, + IN PMDL Mdl, + IN PVOID MapRegisterBase, + IN PVOID CurrentVa, + IN OUT PULONG Length, + IN BOOLEAN WriteToDevice + ) + +/*++ + +Routine Description: + + This routine is invoked to set up the map registers in the DMA controller + to allow a transfer to or from a device. + +Arguments: + + AdapterObject - Pointer to the adapter object representing the DMA + controller channel that has been allocated. + + Mdl - Pointer to the MDL that describes the pages of memory that are + being read or written. + + MapRegisterBase - The address of the base map register that has been + allocated to the device driver for use in mapping the transfer. + + CurrentVa - Current virtual address in the buffer described by the MDL + that the transfer is being done to or from. + + Length - Supplies the length of the transfer. This determines the + number of map registers that need to be written to map the transfer. + Returns the length of the transfer which was actually mapped. + + WriteToDevice - Boolean value that indicates whether this is a write + to the device from memory (TRUE), or vice versa. + +Return Value: + + Returns the logical address that should be used bus master controllers. + +--*/ + +{ + ULONG transferLength; + PHYSICAL_ADDRESS returnAddress; + PULONG pageFrame; + ULONG pageOffset; + + // + // If the adapter is a 32-bit bus master, take the fast path, + // otherwise call HalpMapTransfer for the slow path + // + + if (MapRegisterBase == NULL) { + + pageOffset = BYTE_OFFSET(CurrentVa); + + // + // Calculate how much of the transfer is contiguous + // + transferLength = PAGE_SIZE - pageOffset; + pageFrame = (PULONG)(Mdl+1); + pageFrame += ((ULONG) CurrentVa - (ULONG) Mdl->StartVa) >> PAGE_SHIFT; + + // + // Compute the starting address of the transfer + // + returnAddress.QuadPart = (ULONGLONG)( (*pageFrame << PAGE_SHIFT) + pageOffset); + + // + // If the transfer is not completely contained within + // a page, call the helper to compute the appropriate + // length. + // + if (transferLength < *Length) { + HalpMapTransferHelper(Mdl, CurrentVa, transferLength, pageFrame, Length); + } + return(returnAddress); + } + + return(HalpMapTransfer(AdapterObject, + Mdl, + MapRegisterBase, + CurrentVa, + Length, + WriteToDevice)); + +} + + +VOID +HalpMapTransferHelper( + IN PMDL Mdl, + IN PVOID CurrentVa, + IN ULONG TransferLength, + IN PULONG PageFrame, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + Helper routine for bus master transfers that cross a page + boundary. This routine is separated out from the IoMapTransfer + fast path in order to minimize the total instruction path + length taken for the common network case where the entire + buffer being mapped is contained within one page. + +Arguments: + + Mdl - Pointer to the MDL that describes the pages of memory that are + being read or written. + + CurrentVa - Current virtual address in the buffer described by the MDL + that the transfer is being done to or from. + + TransferLength = Supplies the current transferLength + + PageFrame - Supplies a pointer to the starting page frame of the transfer + + Length - Supplies the length of the transfer. This determines the + number of map registers that need to be written to map the transfer. + Returns the length of the transfer which was actually mapped. + +Return Value: + + None. *Length will be updated + +--*/ + +{ + do { + if (*PageFrame + 1 != *(PageFrame + 1)) { + break; + } + TransferLength += PAGE_SIZE; + PageFrame++; + + } while ( TransferLength < *Length ); + + + // + // Limit the Length to the maximum TransferLength. + // + + if (TransferLength < *Length) { + *Length = TransferLength; + } +} + + +PHYSICAL_ADDRESS +HalpMapTransfer( + IN PADAPTER_OBJECT AdapterObject, + IN PMDL Mdl, + IN PVOID MapRegisterBase, + IN PVOID CurrentVa, + IN OUT PULONG Length, + IN BOOLEAN WriteToDevice + ) + +/*++ + +Routine Description: + + This routine is invoked to set up the map registers in the DMA controller + to allow a transfer to or from a device. + +Arguments: + + AdapterObject - Pointer to the adapter object representing the DMA + controller channel that has been allocated. + + Mdl - Pointer to the MDL that describes the pages of memory that are + being read or written. + + MapRegisterBase - The address of the base map register that has been + allocated to the device driver for use in mapping the transfer. + + CurrentVa - Current virtual address in the buffer described by the MDL + that the transfer is being done to or from. + + Length - Supplies the length of the transfer. This determines the + number of map registers that need to be written to map the transfer. + Returns the length of the transfer which was actually mapped. + + WriteToDevice - Boolean value that indicates whether this is a write + to the device from memory (TRUE), or vice versa. + +Return Value: + + Returns the logical address that should be used bus master controllers. + +--*/ + +{ + BOOLEAN useBuffer; + ULONG transferLength; + ULONG logicalAddress; + PHYSICAL_ADDRESS returnAddress; + ULONG index; + PULONG pageFrame; + PUCHAR bytePointer; + UCHAR adapterMode; + UCHAR dataByte; + PTRANSLATION_ENTRY translationEntry; + ULONG pageOffset; + KIRQL Irql; + + pageOffset = BYTE_OFFSET(CurrentVa); + + // + // Calculate how much of the transfer is contiguous. + // + + transferLength = PAGE_SIZE - pageOffset; + pageFrame = (PULONG)(Mdl+1); + pageFrame += ((ULONG) CurrentVa - (ULONG) Mdl->StartVa) >> PAGE_SHIFT; + logicalAddress = (*pageFrame << PAGE_SHIFT) + pageOffset; + + // + // If the buffer is contigous and does not cross a 64 K bountry then + // just extend the buffer. The 64 K bountry restriction does not apply + // to Eisa systems. + // + + if (HalpEisaDma) { + + while( transferLength < *Length ){ + + if (*pageFrame + 1 != *(pageFrame + 1)) { + break; + } + + transferLength += PAGE_SIZE; + pageFrame++; + + } + + } else { + + while( transferLength < *Length ){ + + if (*pageFrame + 1 != *(pageFrame + 1) || + (*pageFrame & ~0x0f) != (*(pageFrame + 1) & ~0x0f)) { + break; + } + + transferLength += PAGE_SIZE; + pageFrame++; + } + } + + // + // Limit the transferLength to the requested Length. + // + + transferLength = transferLength > *Length ? *Length : transferLength; + + ASSERT(MapRegisterBase != NULL); + + // + // Strip no scatter/gather flag. + // + + translationEntry = (PTRANSLATION_ENTRY) ((ULONG) MapRegisterBase & ~NO_SCATTER_GATHER); + + if ((ULONG) MapRegisterBase & NO_SCATTER_GATHER + && transferLength < *Length) { + + logicalAddress = translationEntry->PhysicalAddress + pageOffset; + translationEntry->Index = COPY_BUFFER; + index = 0; + transferLength = *Length; + useBuffer = TRUE; + + } else { + + // + // If there are map registers, then update the index to indicate + // how many have been used. + // + + useBuffer = FALSE; + index = translationEntry->Index; + translationEntry->Index += ADDRESS_AND_SIZE_TO_SPAN_PAGES( + CurrentVa, + transferLength + ); + } + + // + // It must require memory to be at less than 16 MB. If the + // logical address is greater than 16MB then map registers must be used + // + + if (logicalAddress+transferLength >= MAXIMUM_PHYSICAL_ADDRESS) { + + logicalAddress = (translationEntry + index)->PhysicalAddress + + pageOffset; + useBuffer = TRUE; + + if ((ULONG) MapRegisterBase & NO_SCATTER_GATHER) { + + translationEntry->Index = COPY_BUFFER; + index = 0; + + } + + } + + // + // Copy the data if necessary. + // + + if (useBuffer && WriteToDevice) { + HalpCopyBufferMap( + Mdl, + translationEntry + index, + CurrentVa, + *Length, + WriteToDevice + ); + } + + // + // Return the length. + // + + *Length = transferLength; + + // + // We only support 32 bits, but the return is 64. Just + // zero extend + // + + returnAddress.LowPart = logicalAddress; + returnAddress.HighPart = 0; + + // + // If no adapter was specificed then there is no more work to do so + // return. + // + + if (AdapterObject == NULL || AdapterObject->MasterDevice) { + + return(returnAddress); + } + + // + // Determine the mode based on the transfer direction. + // + + adapterMode = AdapterObject->AdapterMode; + if (WriteToDevice) { + ((PDMA_EISA_MODE) &adapterMode)->TransferType = (UCHAR) WRITE_TRANSFER; + } else { + ((PDMA_EISA_MODE) &adapterMode)->TransferType = (UCHAR) READ_TRANSFER; + + if (AdapterObject->IgnoreCount) { + // + // When the DMA is over there will be no way to tell how much + // data was transfered, so the entire transfer length will be + // copied. To ensure that no stale data is returned to the + // caller zero the buffer before hand. + // + + RtlZeroMemory ( + (PUCHAR) translationEntry[index].VirtualAddress + pageOffset, + transferLength + ); + } + } + + bytePointer = (PUCHAR) &logicalAddress; + + if (AdapterObject->Width16Bits) { + + // + // If this is a 16 bit transfer then adjust the length and the address + // for the 16 bit DMA mode. + // + + transferLength >>= 1; + + // + // In 16 bit DMA mode the low 16 bits are shifted right one and the + // page register value is unchanged. So save the page register value + // and shift the logical address then restore the page value. + // + + dataByte = bytePointer[2]; + logicalAddress >>= 1; + bytePointer[2] = dataByte; + + } + + + // + // grab the spinlock for the system DMA controller + // + + Irql = KfAcquireSpinLock( &AdapterObject->MasterAdapter->SpinLock ); + + // + // Determine the controller number based on the Adapter number. + // + + if (AdapterObject->AdapterNumber == 1) { + + // + // This request is for DMA controller 1 + // + + PDMA1_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 ); + + WRITE_PORT_UCHAR( &dmaControl->Mode, adapterMode ); + + WRITE_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseAddress, + bytePointer[0] + ); + + WRITE_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseAddress, + bytePointer[1] + ); + + WRITE_PORT_UCHAR( + ((PUCHAR) &((PEISA_CONTROL) HalpEisaControlBase)->DmaPageLowPort) + + (ULONG)AdapterObject->PagePort, + bytePointer[2] + ); + + if (HalpEisaDma) { + + // + // Write the high page register with zero value. This enable a special mode + // which allows ties the page register and base count into a single 24 bit + // address register. + // + + WRITE_PORT_UCHAR( + ((PUCHAR) &((PEISA_CONTROL) HalpEisaControlBase)->DmaPageHighPort) + + (ULONG)AdapterObject->PagePort, + 0 + ); + } + + // + // Notify DMA chip of the length to transfer. + // + + WRITE_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseCount, + (UCHAR) ((transferLength - 1) & 0xff) + ); + + WRITE_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseCount, + (UCHAR) ((transferLength - 1) >> 8) + ); + + + // + // Set the DMA chip to read or write mode; and unmask it. + // + + WRITE_PORT_UCHAR( + &dmaControl->SingleMask, + (UCHAR) (DMA_CLEARMASK | AdapterObject->ChannelNumber) + ); + + } else { + + // + // This request is for DMA controller 2 + // + + PDMA2_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 ); + + WRITE_PORT_UCHAR( &dmaControl->Mode, adapterMode ); + + WRITE_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseAddress, + bytePointer[0] + ); + + WRITE_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseAddress, + bytePointer[1] + ); + + WRITE_PORT_UCHAR( + ((PUCHAR) &((PEISA_CONTROL) HalpEisaControlBase)->DmaPageLowPort) + + (ULONG)AdapterObject->PagePort, + bytePointer[2] + ); + + if (HalpEisaDma) { + + // + // Write the high page register with zero value. This enable a special mode + // which allows ties the page register and base count into a single 24 bit + // address register. + // + + WRITE_PORT_UCHAR( + ((PUCHAR) &((PEISA_CONTROL) HalpEisaControlBase)->DmaPageHighPort) + + (ULONG)AdapterObject->PagePort, + 0 + ); + } + + // + // Notify DMA chip of the length to transfer. + // + + WRITE_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseCount, + (UCHAR) ((transferLength - 1) & 0xff) + ); + + WRITE_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseCount, + (UCHAR) ((transferLength - 1) >> 8) + ); + + + // + // Set the DMA chip to read or write mode; and unmask it. + // + + WRITE_PORT_UCHAR( + &dmaControl->SingleMask, + (UCHAR) (DMA_CLEARMASK | AdapterObject->ChannelNumber) + ); + + } + KfReleaseSpinLock (&AdapterObject->MasterAdapter->SpinLock, Irql); + return(returnAddress); +} + +BOOLEAN +IoFlushAdapterBuffers( + IN PADAPTER_OBJECT AdapterObject, + IN PMDL Mdl, + IN PVOID MapRegisterBase, + IN PVOID CurrentVa, + IN ULONG Length, + IN BOOLEAN WriteToDevice + ) + +/*++ + +Routine Description: + + This routine flushes the DMA adapter object buffers. For the Jazz system + its clears the enable flag which aborts the dma. + +Arguments: + + AdapterObject - Pointer to the adapter object representing the DMA + controller channel. + + Mdl - A pointer to a Memory Descriptor List (MDL) that maps the locked-down + buffer to/from which the I/O occured. + + MapRegisterBase - A pointer to the base of the map registers in the adapter + or DMA controller. + + CurrentVa - The current virtual address in the buffer described the the Mdl + where the I/O operation occurred. + + Length - Supplies the length of the transfer. + + WriteToDevice - Supplies a BOOLEAN value that indicates the direction of + the data transfer was to the device. + +Return Value: + + TRUE - No errors are detected so the transfer must succeed. + +--*/ + +{ + PTRANSLATION_ENTRY translationEntry; + PULONG pageFrame; + ULONG transferLength; + ULONG partialLength; + BOOLEAN masterDevice; + + masterDevice = AdapterObject == NULL || AdapterObject->MasterDevice ? + TRUE : FALSE; + + // + // If this is a slave device, then stop the DMA controller. + // + + if (!masterDevice) { + + // + // Mask the DMA request line so that DMA requests cannot occur. + // + + if (AdapterObject->AdapterNumber == 1) { + + // + // This request is for DMA controller 1 + // + + PDMA1_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + WRITE_PORT_UCHAR( + &dmaControl->SingleMask, + (UCHAR) (DMA_SETMASK | AdapterObject->ChannelNumber) + ); + + } else { + + // + // This request is for DMA controller 2 + // + + PDMA2_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + WRITE_PORT_UCHAR( + &dmaControl->SingleMask, + (UCHAR) (DMA_SETMASK | AdapterObject->ChannelNumber) + ); + + } + + } + + if (MapRegisterBase == NULL) { + return(TRUE); + } + + // + // Determine if the data needs to be copied to the orginal buffer. + // This only occurs if the data tranfer is from the device, the + // MapReisterBase is not NULL and the transfer spans a page. + // + + if (!WriteToDevice) { + + // + // Strip no scatter/gather flag. + // + + translationEntry = (PTRANSLATION_ENTRY) ((ULONG) MapRegisterBase & ~NO_SCATTER_GATHER); + + // + // If this is not a master device, then just transfer the buffer. + // + + if ((ULONG) MapRegisterBase & NO_SCATTER_GATHER) { + + if (translationEntry->Index == COPY_BUFFER) { + + if (!masterDevice && !AdapterObject->IgnoreCount) { + // + // Copy only the bytes that have actually been transfered. + // + // + + Length -= HalReadDmaCounter(AdapterObject); + } + + // + // The adapter does not support scatter/gather copy the buffer. + // + + HalpCopyBufferMap( + Mdl, + translationEntry, + CurrentVa, + Length, + WriteToDevice + ); + + } + + } else { + + // + // Cycle through the pages of the transfer to determine if there + // are any which need to be copied back. + // + + transferLength = PAGE_SIZE - BYTE_OFFSET(CurrentVa); + partialLength = transferLength; + pageFrame = (PULONG)(Mdl+1); + pageFrame += ((ULONG) CurrentVa - (ULONG) Mdl->StartVa) >> PAGE_SHIFT; + + while( transferLength <= Length ){ + + if (*pageFrame >= BYTES_TO_PAGES(MAXIMUM_PHYSICAL_ADDRESS)) { + + HalpCopyBufferMap( + Mdl, + translationEntry, + CurrentVa, + partialLength, + WriteToDevice + ); + + } + + (PCCHAR) CurrentVa += partialLength; + partialLength = PAGE_SIZE; + + // + // Note that transferLength indicates the amount which will be + // transfered after the next loop; thus, it is updated with the + // new partial length. + // + + transferLength += partialLength; + pageFrame++; + translationEntry++; + } + + // + // Process the any remaining residue. + // + + partialLength = Length - transferLength + partialLength; + if (partialLength && *pageFrame >= BYTES_TO_PAGES(MAXIMUM_PHYSICAL_ADDRESS)) { + + HalpCopyBufferMap( + Mdl, + translationEntry, + CurrentVa, + partialLength, + WriteToDevice + ); + + } + } + } + + // + // Strip no scatter/gather flag. + // + + translationEntry = (PTRANSLATION_ENTRY) ((ULONG) MapRegisterBase & ~NO_SCATTER_GATHER); + + // + // Clear index in map register. + // + + translationEntry->Index = 0; + + return TRUE; +} + +ULONG +HalReadDmaCounter( + IN PADAPTER_OBJECT AdapterObject + ) +/*++ + +Routine Description: + + This function reads the DMA counter and returns the number of bytes left + to be transfered. + +Arguments: + + AdapterObject - Supplies a pointer to the adapter object to be read. + +Return Value: + + Returns the number of bytes still be be transfered. + +--*/ + +{ + ULONG count; + ULONG high; + KIRQL Irql; + + // + // Grab the spinlock for the system DMA controller. + // + + Irql = KfAcquireSpinLock( &AdapterObject->MasterAdapter->SpinLock ); + + // + // Determine the controller number based on the Adapter number. + // + + if (AdapterObject->AdapterNumber == 1) { + + // + // This request is for DMA controller 1 + // + + PDMA1_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 ); + + + // + // Initialize count to a value which will not match. + // + + count = 0xFFFF00; + + // + // Loop until the same high byte is read twice. + // + + do { + + high = count; + + WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 ); + + // + // Read the current DMA count. + // + + count = READ_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseCount + ); + + count |= READ_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseCount + ) << 8; + + } while ((count & 0xFFFF00) != (high & 0xFFFF00)); + + } else { + + // + // This request is for DMA controller 2 + // + + PDMA2_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 ); + + // + // Initialize count to a value which will not match. + // + + count = 0xFFFF00; + + // + // Loop until the same high byte is read twice. + // + + do { + + high = count; + + WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 ); + + // + // Read the current DMA count. + // + + count = READ_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseCount + ); + + count |= READ_PORT_UCHAR( + &dmaControl->DmaAddressCount[AdapterObject->ChannelNumber] + .DmaBaseCount + ) << 8; + + } while ((count & 0xFFFF00) != (high & 0xFFFF00)); + + + } + + // + // Release the spinlock for the system DMA controller. + // + + KfReleaseSpinLock( &AdapterObject->MasterAdapter->SpinLock, Irql ); + + // + // The DMA counter has a bias of one and can only be 16 bit long. + // + + count = (count + 1) & 0xFFFF; + + // + // If this is a 16 bit dma the multiply the count by 2. + // + + if (AdapterObject->Width16Bits) { + + count *= 2; + + } + + return(count); +} diff --git a/private/ntos/nthals/halx86/i386/ixkdcom.c b/private/ntos/nthals/halx86/i386/ixkdcom.c new file mode 100644 index 000000000..644d3590b --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixkdcom.c @@ -0,0 +1,684 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ixkdcom.c + +Abstract: + + This module contains a very simply package to do com I/O on machines + with standard AT com-ports. It is C code derived from the debugger's + com code. Likely does not work on a PS/2. (Only rewrote the thing + into C so we wouldn't have to deal with random debugger macros.) + + Procedures to init a com object, set and query baud rate, output + character, input character. + +Author: + + Bryan M. Willman (bryanwi) 24-Sep-1990 + +Revision History: + + John Vert (jvert) 12-Jun-1991 + Added ability to check for com-port's existence and hook onto the + highest com-port available. + + John Vert (jvert) 19-Jul-1991 + Moved into HAL + +--*/ + +#include "halp.h" +#include "ixkdcom.h" +#define TIMEOUT_COUNT 1024 * 200 + +UCHAR CpReadLsr (PCPPORT, UCHAR); + +extern BOOLEAN HalpOwnsDisplay; +static UCHAR LastLsr, LastMsr; + + +VOID +CpInitialize ( + PCPPORT Port, + PUCHAR Address, + ULONG Rate + ) + +/*++ + + Routine Description: + + Fill in the com port port object, set the initial baud rate, + turn on the hardware. + + Arguments: + + Port - address of port object + + Address - port address of the com port + (CP_COM1_PORT, CP_COM2_PORT) + + Rate - baud rate (CP_BD_150 ... CP_BD_19200) + +--*/ + +{ + PUCHAR hwport; + UCHAR mcr, ier; + + Port->Address = Address; + Port->Baud = 0; + + CpSetBaud(Port, Rate); + + // + // Assert DTR, RTS. + // + + hwport = Port->Address; + hwport += COM_MCR; + + mcr = MC_DTRRTS; + WRITE_PORT_UCHAR(hwport, mcr); + + hwport = Port->Address; + hwport += COM_IEN; + + ier = 0; + WRITE_PORT_UCHAR(hwport, ier); + +} + + + + +VOID +CpSetBaud ( + PCPPORT Port, + ULONG Rate + ) + +/*++ + + Routine Description: + + Set the baud rate for the port and record it in the port object. + + Arguments: + + Port - address of port object + + Rate - baud rate (CP_BD_150 ... CP_BD_56000) + +--*/ + +{ + ULONG divisorlatch; + PUCHAR hwport; + UCHAR lcr; + + // + // compute the divsor + // + + divisorlatch = CLOCK_RATE / Rate; + + // + // set the divisor latch access bit (DLAB) in the line control reg + // + + hwport = Port->Address; + hwport += COM_LCR; // hwport = LCR register + + lcr = READ_PORT_UCHAR(hwport); + + lcr |= LC_DLAB; + WRITE_PORT_UCHAR(hwport, lcr); + + // + // set the divisor latch value. + // + + hwport = Port->Address; + hwport += COM_DLM; // divisor latch msb + WRITE_PORT_UCHAR(hwport, (UCHAR)((divisorlatch >> 8) & 0xff)); + + hwport--; // divisor latch lsb + WRITE_PORT_UCHAR(hwport, (UCHAR)(divisorlatch & 0xff)); + + + // + // Set LCR to 3. (3 is a magic number in the original assembler) + // + + hwport = Port->Address; + hwport += COM_LCR; + WRITE_PORT_UCHAR(hwport, 3); + + + // + // Remember the baud rate + // + + Port->Baud = Rate; +} + + + +USHORT +CpQueryBaud ( + PCPPORT Port + ) + +/*++ + + Routine Description: + + Return the last value baud rate was set to. + + Arguments: + + Port - address of cpport object which describes the hw port of interest. + + Return Value: + + Baud rate. 0 = none has been set. + +--*/ + +{ + return (USHORT) Port->Baud; +} + +VOID +CpSendModemString ( + PCPPORT Port, + IN PUCHAR String + ) +/*++ + + Routine Description: + + Sends a command string to the modem. + This is down in order to aid the modem in determining th + baud rate the local connect is at. + + Arguments: + + Port - Address of CPPORT + String - String to send to modem + +--*/ +{ + static ULONG Delay; + TIME_FIELDS CurrentTime; + UCHAR i; + ULONG l; + + if (Port->Flags & PORT_SENDINGSTRING) + return ; + + Port->Flags |= PORT_SENDINGSTRING; + if (!Delay) { + // see how long 1 second is + HalQueryRealTimeClock (&CurrentTime); + l = CurrentTime.Second; + while (l == (ULONG) CurrentTime.Second) { + CpReadLsr(Port, 0); + HalQueryRealTimeClock (&CurrentTime); + Delay++; + } + Delay = Delay / 3; + } + + l = Delay; + while (*String) { + HalQueryRealTimeClock (&CurrentTime); + i = CpReadLsr (Port, 0); + if (i & COM_OUTRDY) { + if ((--l) == 0) { + WRITE_PORT_UCHAR(Port->Address+COM_DAT, *String); + String++; + l = Delay; + } + } + if (i & COM_DATRDY) + READ_PORT_UCHAR(Port->Address + COM_DAT); + } + Port->Flags &= ~PORT_SENDINGSTRING; +} + +UCHAR +CpReadLsr ( + PCPPORT Port, + UCHAR waiting + ) + +/*++ + + Routine Description: + + Read LSR byte from specified port. If HAL owns port & display + it will also cause a debug status to be kept up to date. + + Handles entering & exiting modem control mode for debugger. + + Arguments: + + Port - Address of CPPORT + + Returns: + + Byte read from port + +--*/ +{ + static UCHAR ringflag = 0; + static UCHAR diagout[3]; + static ULONG diagmsg[3] = { 'TRP ', 'LVO ', 'MRF ' }; + static UCHAR ModemString[] = "\n\rAT\n\r"; + TIME_FIELDS CurrentTime; + UCHAR lsr, msr, i; + ULONG diagstr[12]; + + lsr = READ_PORT_UCHAR(Port->Address + COM_LSR); + + if (lsr & COM_PE) + diagout[0] = 8; // Parity error + + if (lsr & COM_OE) + diagout[1] = 8; // Overflow error + + if (lsr & COM_FE) + diagout[2] = 8; // Framing error + + if (lsr & waiting) { + LastLsr = ~COM_DATRDY | (lsr & COM_DATRDY); + return lsr; + } + + msr = READ_PORT_UCHAR (Port->Address + COM_MSR); + + if (Port->Flags & PORT_MODEMCONTROL) { + if (msr & SERIAL_MSR_DCD) { + + // + // In modem control mode with carrier detect + // Reset carrier lost time + // + + Port->Flags |= PORT_NOCDLTIME | PORT_MDM_CD; + + } else { + + // + // In modem control mode, but no carrier detect. After + // 60 seconds drop out of modem control mode + // + + if (Port->Flags & PORT_NOCDLTIME) { + HalQueryRealTimeClock (&Port->CarrierLostTime); + Port->Flags &= ~PORT_NOCDLTIME; + ringflag = 0; + } + + HalQueryRealTimeClock (&CurrentTime); + if (CurrentTime.Minute != Port->CarrierLostTime.Minute && + CurrentTime.Second >= Port->CarrierLostTime.Second) { + + // + // It's been at least 60 seconds - drop out of + // modem control mode until next RI + // + + Port->Flags &= ~PORT_MODEMCONTROL; + CpSendModemString (Port, ModemString); + } + + if (Port->Flags & PORT_MDM_CD) { + + // + // We had a connection - if it's the connection has been + // down for a few seconds, then send a string to the modem + // + + if (CurrentTime.Second < Port->CarrierLostTime.Second) + CurrentTime.Second += 60; + + if (CurrentTime.Second > Port->CarrierLostTime.Second + 10) { + Port->Flags &= ~PORT_MDM_CD; + CpSendModemString (Port, ModemString); + } + } + } + } + + if ((lsr == LastLsr && msr == LastMsr) || !(Port->Flags & PORT_SAVED)) + return lsr; + + ringflag |= (msr & SERIAL_MSR_RI) ? 1 : 2; + if (ringflag == 3) { + + // + // The ring indicate line has toggled + // Use modem control from now on + // + + ringflag = 0; + Port->Flags |= PORT_MODEMCONTROL | PORT_NOCDLTIME; + Port->Flags &= ~PORT_MDM_CD; + + if (Port->Flags & PORT_DEFAULTRATE && Port->Baud != BD_9600) { + + // + // Baud rate was never specified switch + // to 9600 baud as default (for modem usage). + // + + HalDisplayString (MSG_DEBUG_9600); + CpSetBaud (Port, BD_9600); + //Port->Flags |= PORT_DISBAUD; + } + } + + for (i=0; i < 3; i++) { + if (diagout[i]) { + diagout[i]--; + diagstr[10-i] = diagmsg[i]; + } else { + diagstr[10-i] = ' '; + } + } + + diagstr[7] = (LastLsr & COM_DATRDY) ? 'VCR ' : ' '; + diagstr[6] = (lsr & COM_OUTRDY) ? ' ' : 'DNS '; + diagstr[5] = (msr & 0x10) ? 'STC ' : ' '; + diagstr[4] = (msr & 0x20) ? 'RSD ' : ' '; + diagstr[3] = (msr & 0x40) ? ' IR ' : ' '; + diagstr[2] = (msr & 0x80) ? ' DC ' : ' '; + diagstr[1] = (Port->Flags & PORT_MODEMCONTROL) ? 'MDM ' : ' '; + diagstr[0] = ' '; +#if 0 + if (Port->Flags & PORT_DISBAUD) { + switch (Port->Baud) { + case BD_9600: diagstr[0] = ' 69 '; break; + case BD_14400: diagstr[0] = 'K41 '; break; + case BD_19200: diagstr[0] = 'K91 '; break; + case BD_56000: diagstr[0] = 'K65 '; break; + } + } +#endif + + HalpDisplayDebugStatus ((PUCHAR) diagstr, 11*4); + LastLsr = lsr; + LastMsr = msr; + return lsr; +} + + + + +VOID +CpPutByte ( + PCPPORT Port, + UCHAR Byte + ) + +/*++ + + Routine Description: + + Write a byte out to the specified com port. + + Arguments: + + Port - Address of CPPORT object + + Byte - data to emit + +--*/ + +{ + UCHAR msr, lsr; + + // + // If modem control, make sure DSR, CTS and CD are all set before + // sending any data. + // + + while ((Port->Flags & PORT_MODEMCONTROL) && + (msr = READ_PORT_UCHAR(Port->Address + COM_MSR) & MS_DSRCTSCD) != MS_DSRCTSCD) { + + // + // If no CD, and there's a charactor ready, eat it + // + + lsr = CpReadLsr (Port, 0); + if ((msr & MS_CD) == 0 && (lsr & COM_DATRDY) == COM_DATRDY) { + READ_PORT_UCHAR(Port->Address + COM_DAT); + } + } + + // + // Wait for port to not be busy + // + + while (!(CpReadLsr(Port, COM_OUTRDY) & COM_OUTRDY)) ; + + // + // Send the byte + // + + WRITE_PORT_UCHAR(Port->Address + COM_DAT, Byte); +} + +USHORT +CpGetByte ( + PCPPORT Port, + PUCHAR Byte, + BOOLEAN WaitForByte + ) + +/*++ + + Routine Description: + + Fetch a byte and return it. + + Arguments: + + Port - address of port object that describes hw port + + Byte - address of variable to hold the result + + WaitForByte - flag indicates wait for byte or not. + + Return Value: + + CP_GET_SUCCESS if data returned. + + CP_GET_NODATA if no data available, but no error. + + CP_GET_ERROR if error (overrun, parity, etc.) + +--*/ + +{ + UCHAR lsr; + UCHAR value; + ULONG limitcount; + + // + // Make sure DTR and CTS are set + // + // (What does CTS have to do with reading from a full duplex line???) + + + // + // Check to make sure the CPPORT we were passed has been initialized. + // (The only time it won't be initialized is when the kernel debugger + // is disabled, in which case we just return.) + // + if (Port->Address == NULL) { + return(CP_GET_NODATA); + } + + limitcount = WaitForByte ? TIMEOUT_COUNT : 1; + while (limitcount != 0) { + limitcount--; + + lsr = CpReadLsr(Port, COM_DATRDY); + if ((lsr & COM_DATRDY) == COM_DATRDY) { + + // + // Check for errors + // + if (lsr & (COM_FE | COM_PE | COM_OE)) { + *Byte = 0; + return(CP_GET_ERROR); + } + + // + // fetch the byte + // + + value = READ_PORT_UCHAR(Port->Address + COM_DAT); + + if (Port->Flags & PORT_MODEMCONTROL) { + + // + // Using modem control. If no CD, then skip this byte. + // + + if ((READ_PORT_UCHAR(Port->Address + COM_MSR) & MS_CD) == 0) { + continue; + } + } + + *Byte = value & (UCHAR)0xff; + return CP_GET_SUCCESS; + } + } + + LastLsr = 0; + CpReadLsr (Port, 0); + return CP_GET_NODATA; +} + + + +BOOLEAN +CpDoesPortExist( + IN PUCHAR Address + ) + +/*++ + +Routine Description: + + This routine will attempt to place the port into its + diagnostic mode. If it does it will twiddle a bit in + the modem control register. If the port exists this + twiddling should show up in the modem status register. + + NOTE: This routine must be called before the device is + enabled for interrupts, this includes setting the + output2 bit in the modem control register. + + This is blatantly stolen from TonyE's code in ntos\dd\serial\serial.c. + +Arguments: + + Address - address of hw port. + +Return Value: + + TRUE - Port exists. Party on. + + FALSE - Port doesn't exist. Don't use it. + +--*/ + +{ + UCHAR OldModemStatus; + UCHAR ModemStatus; + BOOLEAN ReturnValue = TRUE; + + // + // Save the old value of the modem control register. + // + + OldModemStatus = READ_PORT_UCHAR(Address+COM_MCR); + + // + // Set the port into diagnostic mode. + // + + WRITE_PORT_UCHAR( + Address+COM_MCR, + SERIAL_MCR_LOOP + ); + + // + // Bang on it again to make sure that all the lower bits + // are clear. + // + + WRITE_PORT_UCHAR( + Address+COM_MCR, + SERIAL_MCR_LOOP + ); + + // + // Read the modem status register. The high for bits should + // be clear. + // + + ModemStatus = READ_PORT_UCHAR(Address+COM_MSR); + + if (ModemStatus & (SERIAL_MSR_CTS | SERIAL_MSR_DSR | + SERIAL_MSR_RI | SERIAL_MSR_DCD)) { + + ReturnValue = FALSE; + goto AllDone; + + } + + // + // So far so good. Now turn on OUT1 in the modem control register + // and this should turn on ring indicator in the modem status register. + // + + WRITE_PORT_UCHAR( + Address+COM_MCR, + (SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP) + ); + + ModemStatus = READ_PORT_UCHAR(Address+COM_MSR); + + if (!(ModemStatus & SERIAL_MSR_RI)) { + + ReturnValue = FALSE; + goto AllDone; + + } + +AllDone: ; + + // + // Put the modem control back into a clean state. + // + + WRITE_PORT_UCHAR( + Address+COM_MCR, + OldModemStatus + ); + + return ReturnValue; + +} + diff --git a/private/ntos/nthals/halx86/i386/ixkdcom.h b/private/ntos/nthals/halx86/i386/ixkdcom.h new file mode 100644 index 000000000..40171040a --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixkdcom.h @@ -0,0 +1,165 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ixkdcom.h + +Abstract: + + This module contains the header file for a very simple com port package. + +Author: + + Bryan M. Willman (bryanwi) 24-Sep-1990 + +Revision History: + + John Vert (jvert) 19-Jul-1991 + Moved into HAL +--*/ + +#define COM1_PORT 0x03f8 +#define COM2_PORT 0x02f8 + +#define COM_DAT 0x00 +#define COM_IEN 0x01 // interrupt enable register +#define COM_LCR 0x03 // line control registers +#define COM_MCR 0x04 // modem control reg +#define COM_LSR 0x05 // line status register +#define COM_MSR 0x06 // modem status register +#define COM_DLL 0x00 // divisor latch least sig +#define COM_DLM 0x01 // divisor latch most sig + +#define COM_BI 0x10 +#define COM_FE 0x08 +#define COM_PE 0x04 +#define COM_OE 0x02 + +#define LC_DLAB 0x80 // divisor latch access bit + +#define CLOCK_RATE 0x1C200 // USART clock rate + +#define MC_DTRRTS 0x03 // Control bits to assert DTR and RTS +#define MS_DSRCTSCD 0xB0 // Status bits for DSR, CTS and CD +#define MS_CD 0x80 + +#define BD_150 150 +#define BD_300 300 +#define BD_600 600 +#define BD_1200 1200 +#define BD_2400 2400 +#define BD_4800 4800 +#define BD_9600 9600 +#define BD_14400 14400 +#define BD_19200 19200 +#define BD_56000 56000 + +#define COM_OUTRDY 0x20 +#define COM_DATRDY 0x01 + + +// +// This bit controls the loopback testing mode of the device. Basically +// the outputs are connected to the inputs (and vice versa). +// +#define SERIAL_MCR_LOOP 0x10 + +// +// This bit is used for general purpose output. +// +#define SERIAL_MCR_OUT1 0x04 + +// +// This bit contains the (complemented) state of the clear to send +// (CTS) line. +// +#define SERIAL_MSR_CTS 0x10 + +// +// This bit contains the (complemented) state of the data set ready +// (DSR) line. +// +#define SERIAL_MSR_DSR 0x20 + +// +// This bit contains the (complemented) state of the ring indicator +// (RI) line. +// +#define SERIAL_MSR_RI 0x40 + +// +// This bit contains the (complemented) state of the data carrier detect +// (DCD) line. +// +#define SERIAL_MSR_DCD 0x80 + +typedef struct _CPPORT { + PUCHAR Address; + ULONG Baud; + USHORT Flags; + TIME_FIELDS CarrierLostTime; +// ULONG LockVar; +// KSPIN_LOCK Lock; +} CPPORT, *PCPPORT; + +#define PORT_DEFAULTRATE 0x0001 // baud rate not specified, using default +#define PORT_MODEMCONTROL 0x0002 // using modem controls +#define PORT_SAVED 0x0004 // port is in saved state +#define PORT_NOCDLTIME 0x0010 // 'Carrier detect lost' time not set +#define PORT_DISBAUD 0x0020 // Display baud rate abbrv +#define PORT_SENDINGSTRING 0x0040 // Sending modem string (don't recurse) +#define PORT_MDM_CD 0x0080 // CD while in modem control mode + +VOID +CpInitialize ( + PCPPORT Port, + PUCHAR Address, + ULONG Rate + ); + +VOID +CpSetBaud ( + PCPPORT Port, + ULONG Rate + ); + +USHORT +CpQueryBaud ( + PCPPORT Port + ); + +VOID +CpPutByte ( + PCPPORT Port, + UCHAR Byte + ); + +USHORT +CpGetByte ( + PCPPORT Port, + PUCHAR Byte, + BOOLEAN WaitForData + ); + +VOID +CpLockPort ( + PCPPORT Port + ); + +VOID +CpUnlockPort ( + PCPPORT Port + ); + +VOID +CpStallExecution ( + VOID + ); + +BOOLEAN +CpDoesPortExist( + IN PUCHAR Address + ); + diff --git a/private/ntos/nthals/halx86/i386/ixlock.asm b/private/ntos/nthals/halx86/i386/ixlock.asm new file mode 100644 index 000000000..98cf9c425 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixlock.asm @@ -0,0 +1,475 @@ + title "Irql Processing" +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; ixlock.asm +; +; Abstract: +; +; This module implements various locking functions optimized for this hal. +; +; Author: +; +; Ken Reneris (kenr) 21-April-1994 +; +; Environment: +; +; Kernel mode only. +; +; Revision History: +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\ix8259.inc +include i386\kimacro.inc +include mac386.inc + .list + + EXTRNP _KeBugCheck,1,IMPORT + EXTRNP _KeSetEventBoostPriority, 2, IMPORT + EXTRNP _KeWaitForSingleObject,5, IMPORT + + extrn FindHigherIrqlMask:DWORD + extrn SWInterruptHandlerTable:DWORD + + EXTRNP _KeRaiseIrql,2 + EXTRNP _KeLowerIrql,1 + +ifdef NT_UP + LOCK_ADD equ add + LOCK_DEC equ dec +else + LOCK_ADD equ lock add + LOCK_DEC equ lock dec +endif + + page ,132 + subttl "AcquireSpinLock" + +_TEXT$01 SEGMENT PARA PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING + +;++ +; +; KIRQL +; KfAcquireSpinLock ( +; IN PKSPIN_LOCK SpinLock +; ) +; +; Routine Description: +; +; This function raises to DISPATCH_LEVEL and then acquires a the +; kernel spin lock. +; +; In a UP hal spinlock serialization is accomplished by raising the +; IRQL to DISPATCH_LEVEL. The SpinLock is not used; however, for +; debugging purposes if the UP hal is compiled with the NT_UP flag +; not set (ie, MP) we take the SpinLock. +; +; Arguments: +; +; (ecx) = SpinLock Supplies a pointer to an kernel spin lock. +; +; Return Value: +; +; OldIrql +; +;-- + +cPublicFastCall KfAcquireSpinLock,1 +cPublicFpo 0,0 + + xor eax, eax ; Eliminate partial stall on return to caller + mov al, PCR[PcIrql] ; (al) = Old Irql + mov byte ptr PCR[PcIrql], DISPATCH_LEVEL ; set new irql + +ifndef NT_UP +asl10: ACQUIRE_SPINLOCK ecx, +endif + +ifdef IRQL_METRICS + inc HalRaiseIrqlCount +endif +if DBG + cmp al, DISPATCH_LEVEL ; old > new? + ja short asl99 ; yes, go bugcheck +endif + fstRET KfAcquireSpinLock + +ifndef NT_UP +asl20: SPIN_ON_SPINLOCK ecx, +endif + +if DBG +cPublicFpo 2,1 +asl99: movzx eax, al + push eax ; put old irql where we can find it + stdCall _KeBugCheck, ; never return +endif + fstRET KfAcquireSpinLock +fstENDP KfAcquireSpinLock + +;++ +; +; KIRQL +; KeAcquireSpinLockRaiseToSynch ( +; IN PKSPIN_LOCK SpinLock +; ) +; +; Routine Description: +; +; This function acquires the SpinLock at SYNCH_LEVEL. The function +; is optmized for hoter locks (the lock is tested before acquired. +; Any spin should occur at OldIrql; however, since this is a UP hal +; we don't have the code for it) +; +; In a UP hal spinlock serialization is accomplished by raising the +; IRQL to SYNCH_LEVEL. The SpinLock is not used; however, for +; debugging purposes if the UP hal is compiled with the NT_UP flag +; not set (ie, MP) we take the SpinLock. +; +; Arguments: +; +; (ecx) = SpinLock Supplies a pointer to an kernel spin lock. +; +; Return Value: +; +; OldIrql +; +;-- + +cPublicFastCall KeAcquireSpinLockRaiseToSynch,1 +cPublicFpo 0,0 + + mov al, PCR[PcIrql] ; (al) = Old Irql + mov byte ptr PCR[PcIrql], SYNCH_LEVEL ; set new irql + +ifndef NT_UP +asls10: ACQUIRE_SPINLOCK ecx, +endif + +ifdef IRQL_METRICS + inc HalRaiseIrqlCount +endif +if DBG + cmp al, SYNCH_LEVEL ; old > new? + ja short asls99 ; yes, go bugcheck +endif + fstRET KeAcquireSpinLockRaiseToSynch + +ifndef NT_UP +asls20: SPIN_ON_SPINLOCK ecx, +endif + +if DBG +cPublicFpo 2,1 +asls99: movzx eax, al + push eax ; put old irql where we can find it + stdCall _KeBugCheck, ; never return +endif + fstRET KeAcquireSpinLockRaiseToSynch +fstENDP KeAcquireSpinLockRaiseToSynch + + PAGE + SUBTTL "Release Kernel Spin Lock" + +;++ +; +; VOID +; KfReleaseSpinLock ( +; IN PKSPIN_LOCK SpinLock, +; IN KIRQL NewIrql +; ) +; +; Routine Description: +; +; This function releases a kernel spin lock and lowers to the new irql +; +; In a UP hal spinlock serialization is accomplished by raising the +; IRQL to DISPATCH_LEVEL. The SpinLock is not used; however, for +; debugging purposes if the UP hal is compiled with the NT_UP flag +; not set (ie, MP) we use the SpinLock. +; +; Arguments: +; +; (ecx) = SpinLock Supplies a pointer to an executive spin lock. +; (dl) = NewIrql New irql value to set +; +; Return Value: +; +; None. +; +;-- + +align 16 +cPublicFastCall KfReleaseSpinLock ,2 +cPublicFpo 0,0 +ifndef NT_UP + RELEASE_SPINLOCK ecx ; release it +endif + xor ecx, ecx +if DBG + cmp dl, PCR[PcIrql] + ja short rsl99 +endif + pushfd + cli + mov PCR[PcIrql], dl ; store old irql + mov cl, dl ; (ecx) = 32bit extended OldIrql + mov edx, PCR[PcIRR] + and edx, FindHigherIrqlMask[ecx*4] ; (edx) is the bitmask of + ; pending interrupts we need to + jne short rsl20 ; dispatch now. + + popfd + fstRet KfReleaseSpinLock ; all done + +if DBG +rsl99: stdCall _KeBugCheck, ; never return +endif + +cPublicFpo 0,1 +rsl20: bsr ecx, edx ; (ecx) = Pending irq level + cmp ecx, DISPATCH_LEVEL + jle short rsl40 + + mov eax, PCR[PcIDR] ; Clear all the interrupt + SET_8259_MASK ; masks +rsl40: + mov edx, 1 + shl edx, cl + xor PCR[PcIRR], edx ; clear bit in IRR + call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int. + popfd + +cPublicFpo 0, 0 + fstRet KfReleaseSpinLock ; all done + +fstENDP KfReleaseSpinLock + +;++ +; +; VOID +; FASTCALL +; ExAcquireFastMutex ( +; IN PFAST_MUTEX FastMutex +; ) +; +; Routine description: +; +; This function acquire ownership of the FastMutex +; +; Arguments: +; +; (ecx) = FastMutex - Supplies a pointer to the fast mutex +; +; Return Value: +; +; None. +; +;-- + +cPublicFastCall ExAcquireFastMutex,1 +cPublicFpo 0,0 + mov al, PCR[PcIrql] ; (cl) = OldIrql +if DBG + cmp al, APC_LEVEL ; Is OldIrql > NewIrql? + ja short afm99 ; Yes, bugcheck + + mov edx, PCR[PcPrcb] + mov edx, [edx].PbCurrentThread ; (edx) = Current Thread + cmp [ecx].FmOwner, edx ; Already owned by this thread? + je short afm98 ; Yes, error +endif + + mov byte ptr PCR[PcIrql], APC_LEVEL ; Set NewIrql + LOCK_DEC dword ptr [ecx].FmCount ; Get count + jz short afm_ret ; The owner? Yes, Done + + inc dword ptr [ecx].FmContention + +cPublicFpo 0,2 + push eax ; save OldIrql + push ecx ; Save FAST_MUTEX + add ecx, FmEvent ; Wait on Event + + stdCall _KeWaitForSingleObject, + + pop ecx ; (ecx) = FAST_MUTEX + pop eax ; (al) = OldIrql + +cPublicFpo 1,0 +afm_ret: + +if DBG + cli + mov edx, PCR[PcPrcb] + mov edx, [edx].PbCurrentThread ; (edx) = Current Thread + sti + mov [ecx].FmOwner, edx ; Save in Fast Mutex +endif + mov byte ptr [ecx].FmOldIrql, al + fstRet ExAcquireFastMutex + +if DBG +afm98: stdCall _KeBugCheck, ; never return +afm99: stdCall _KeBugCheck, ; never return + fstRet ExAcquireFastMutex +endif + +fstENDP ExAcquireFastMutex + + +;++ +; +; VOID +; FASTCALL +; ExReleaseFastMutex ( +; IN PFAST_MUTEX FastMutex +; ) +; +; Routine description: +; +; This function releases ownership of the FastMutex +; +; Arguments: +; +; (ecx) = FastMutex - Supplies a pointer to the fast mutex +; +; Return Value: +; +; None. +; +;-- + +cPublicFastCall ExReleaseFastMutex,1 +cPublicFpo 0,0 + xor eax, eax +if DBG + cli + mov edx, PCR[PcPrcb] + mov edx, [edx].PbCurrentThread ; (edx) = CurrentThread + sti + cmp [ecx].FmOwner, edx ; Owner == CurrentThread? + jne short rfm_threaderror ; No, bugcheck +endif + or byte ptr [ecx].FmOwner, 1 ; not the owner anymore + + mov al, byte ptr [ecx].FmOldIrql ; (eax) = OldIrql + LOCK_ADD dword ptr [ecx].FmCount, 1 ; Remove our count + js short rfm05 ; if < 0, set event + jnz short rfm10 ; if != 0, don't set event + +rfm05: +cPublicFpo 0,2 + push eax ; Save OldIrql + add ecx, FmEvent + stdCall _KeSetEventBoostPriority, + pop eax + +cPublicFpo 0,0 +rfm10: + cli + mov PCR[PcIrql], eax + mov edx, PCR[PcIRR] + and edx, FindHigherIrqlMask[eax*4] ; (edx) is the bitmask of + ; pending interrupts we need to + jne short rfm20 ; dispatch now. + + sti + fstRet ExReleaseFastMutex ; all done +if DBG +rfm_threaderror: + stdCall _KeBugCheck, +endif + +rfm20: bsr ecx, edx ; (ecx) = Pending irq level + cmp ecx, DISPATCH_LEVEL + jle short rfm40 + + mov eax, PCR[PcIDR] ; Clear all the interrupt + SET_8259_MASK ; masks +rfm40: + mov edx, 1 + shl edx, cl + xor PCR[PcIRR], edx ; clear bit in IRR + call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int. + sti + fstRet ExReleaseFastMutex ; all done +fstENDP ExReleaseFastMutex + +;++ +; +; BOOLEAN +; FASTCALL +; ExTryToAcquireFastMutex ( +; IN PFAST_MUTEX FastMutex +; ) +; +; Routine description: +; +; This function acquire ownership of the FastMutex +; +; Arguments: +; +; (ecx) = FastMutex - Supplies a pointer to the fast mutex +; +; Return Value: +; +; Returns TRUE if the FAST_MUTEX was acquired; otherwise false +; +;-- + +cPublicFastCall ExTryToAcquireFastMutex,1 +cPublicFpo 0,0 + mov al, PCR[PcIrql] ; (al) = OldIrql + +if DBG + cmp al, APC_LEVEL ; Is OldIrql > NewIrql? + ja short tam99 ; Yes, bugcheck +endif + +; +; Try to acquire - but needs to support 386s. +; *** Warning: This code is NOT MP safe *** +; But, we know that this hal really only runs on UP machines +; + cli + cmp dword ptr [ecx].FmCount, 1 ; Busy? + jne short tam20 ; Yes, abort + + mov dword ptr [ecx].FmCount, 0 ; acquire count + +if DBG + mov edx, PCR[PcPrcb] + mov edx, [edx].PbCurrentThread ; (edx) = Current Thread + mov [ecx].FmOwner, edx ; Save in Fast Mutex +endif + mov PCR[PcIrql], APC_LEVEL + sti + mov byte ptr [ecx].FmOldIrql, al + mov eax, 1 ; return TRUE + fstRet ExTryToAcquireFastMutex + +tam20: sti + xor eax, eax ; return FALSE + fstRet ExTryToAcquireFastMutex ; all done + +if DBG +tam99: stdCall _KeBugCheck, ; never return + xor eax, eax ; return FALSE + fstRet ExTryToAcquireFastMutex +endif + +fstENDP ExTryToAcquireFastMutex + +_TEXT$01 ends + + end diff --git a/private/ntos/nthals/halx86/i386/ixmca.c b/private/ntos/nthals/halx86/i386/ixmca.c new file mode 100644 index 000000000..eea051e49 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixmca.c @@ -0,0 +1,868 @@ +/*++ + +Module Name: + + ixmca.c + +Abstract: + + HAL component of the Machine Check Architecture. + All exported MCA functionality is present in this file. + +Author: + + Srikanth Kambhatla (Intel) + +Revision History: + + Anil Aggarwal (Intel) + Changes incorporated as per design review with Microsoft + +--*/ + +#include +#include + +// +// Structure to keep track of MCA features available on installed hardware +// + +typedef struct _MCA_INFO { + FAST_MUTEX Mutex; + UCHAR NumBanks; // Number of Banks present + ULONGLONG Bank0Config; // Bank0 configuration setup by BIOS. + // This will be used as mask when + // setting up bank 0 + MCA_DRIVER_INFO DriverInfo; // Info about registered driver + KDPC Dpc; // DPC object for MCA + +} MCA_INFO, *PMCA_INFO; + + +// +// Default MCA Bank configuration +// +#define MCA_DEFAULT_BANK_CONF 0xFFFFFFFFFFFFFFFF + +// +// MCA architecture related defines +// + +#define MCA_NUM_REGS 4 +#define MCA_CNT_MASK 0xFF +#define MCG_CTL_PRESENT 0x100 + +#define MCE_VALID 0x01 + +// +// MSR register addresses for MCA +// + +#define MCG_CAP 0x179 +#define MCG_STATUS 0x17a +#define MCG_CTL 0x17b +#define MC0_CTL 0x400 +#define MC0_STATUS 0x401 +#define MC0_ADDR 0x402 +#define MC0_MISC 0x403 + +#define PENTIUM_MC_ADDR 0x0 +#define PENTIUM_MC_TYPE 0x1 + +// +// Writing all 1's to MCG_CTL register enables logging. +// +#define MCA_MCGCTL_ENABLE_LOGGING 0xffffffff + +// +// Bit interpretation of MCG_STATUS register +// +#define MCG_MC_INPROGRESS 0x4 +#define MCG_EIP_VALID 0x2 +#define MCG_RESTART_EIP_VALID 0x1 + +// +// For the function that reads the error reporting bank log, the type of error we +// are interested in +// +#define MCA_GET_ANY_ERROR 0x1 +#define MCA_GET_NONRESTARTABLE_ERROR 0x2 + + +// +// Global Varibles +// + +MCA_INFO HalpMcaInfo; +extern KAFFINITY HalpActiveProcessors; +extern UCHAR HalpClockMcaQueueDpc; + +extern UCHAR MsgMCEPending[]; +extern WCHAR rgzSessionManager[]; +extern WCHAR rgzEnableMCE[]; +extern WCHAR rgzEnableMCA[]; + + +// +// External prototypes +// + +VOID +HalpMcaCurrentProcessorSetTSS ( + VOID + ); + +VOID +HalpSetCr4MCEBit ( + VOID + ); + +// +// Internal prototypes +// + +VOID +HalpMcaInit ( + VOID + ); + +NTSTATUS +HalpMcaReadProcessorException ( + OUT PMCA_EXCEPTION Exception, + IN BOOLEAN NonRestartableOnly + ); + +VOID +HalpMcaCurrentProcessorSetConfig ( + VOID + ); + +VOID +HalpMcaQueueDpc( + VOID + ); + +VOID +HalpMcaGetConfiguration ( + OUT PULONG MCEEnabled, + OUT PULONG MCAEnabled + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, HalpMcaInit) +#pragma alloc_text(INIT, HalpMcaCurrentProcessorSetConfig) +#pragma alloc_text(INIT, HalpMcaGetConfiguration) +#pragma alloc_text(PAGE, HalpGetMcaLog) +#pragma alloc_text(PAGE, HalpMcaRegisterDriver) +#endif + + +// +// All the initialization code for MCA goes here +// + +VOID +HalpMcaInit ( + VOID + ) + +/*++ + Routine Description: + This routine is called to do all the initialization work + + Arguments: + None + + Return Value: + STATUS_SUCCESS if successful + error status otherwise +--*/ + +{ + ULONGLONG MsrCapability; + KIRQL OldIrql; + PKTHREAD Thread; + KAFFINITY ActiveProcessors, CurrentAffinity; + ULONGLONG MsrMceType; + ULONG MCEEnabled; + ULONG MCAEnabled; + + if ( (!(HalpFeatureBits & HAL_MCE_PRESENT)) && + (!(HalpFeatureBits & HAL_MCA_PRESENT)) ) { + + return; // nothing to do + } + + HalpMcaGetConfiguration(&MCEEnabled, &MCAEnabled); + + if ( (HalpFeatureBits & HAL_MCE_PRESENT) && + (!(HalpFeatureBits & HAL_MCA_PRESENT)) ) { + + if (MCEEnabled == FALSE) { + + // User has not enabled MCE capability. + HalpFeatureBits &= ~(HAL_MCE_PRESENT | HAL_MCA_PRESENT); + + return; + } + +#if DBG + DbgPrint("MCE feature is enabled via registry\n"); +#endif // DBG + + MsrMceType = RDMSR(PENTIUM_MC_TYPE); + + if (((PLARGE_INTEGER)(&MsrMceType))->LowPart & MCE_VALID) { + + // + // On an AST PREMMIA MX machine we seem to have a Machine Check Pending + // always. + // + + HalDisplayString(MsgMCEPending); + + HalpFeatureBits &= ~(HAL_MCE_PRESENT | HAL_MCA_PRESENT); + + return; + } + } + + // + // If MCA is available, find out the number of banks available and + // also get the platform specific bank 0 configuration + // + + if ( HalpFeatureBits & HAL_MCA_PRESENT ) { + + if (MCAEnabled == FALSE) { + + /* User has disabled MCA capability. */ +#if DBG + DbgPrint("MCA feature is disabled via registry\n"); +#endif // DBG + + HalpFeatureBits &= ~(HAL_MCE_PRESENT | HAL_MCA_PRESENT); + return; + } + + MsrCapability = RDMSR(MCG_CAP); + HalpMcaInfo.NumBanks = (UCHAR)(MsrCapability & MCA_CNT_MASK); + + // + // Find out the Bank 0 configuration setup by BIOS. This will be used + // as a mask when writing to Bank 0 + // + + HalpMcaInfo.Bank0Config = RDMSR(MC0_CTL); + } + + if ( (HalpFeatureBits & HAL_MCA_PRESENT) || + (HalpFeatureBits & HAL_MCE_PRESENT) ) { + + ASSERT(HalpFeatureBits & HAL_MCE_PRESENT); + + // + // This lock synchronises access to the log area when we call the + // logger on multiple processors. + // + + ExInitializeFastMutex (&HalpMcaInfo.Mutex); + + // + // Initialize on each processor + // + + Thread = KeGetCurrentThread (); + ActiveProcessors = HalpActiveProcessors; + for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) { + + if (ActiveProcessors & CurrentAffinity) { + ActiveProcessors &= ~CurrentAffinity; + KeSetAffinityThread (Thread, CurrentAffinity); + + // + // Initialize MCA support on this processor + // + + OldIrql = KfRaiseIrql(HIGH_LEVEL); + + HalpMcaCurrentProcessorSetTSS(); + HalpMcaCurrentProcessorSetConfig(); + + KfLowerIrql(OldIrql); + } + } + + // + // Restore threads affinity + // + + KeSetAffinityThread (Thread, HalpActiveProcessors); + } +} + + +VOID +HalpMcaCurrentProcessorSetConfig ( + VOID + ) +/*++ + + Routine Description: + + This routine sets/modifies the configuration of the machine check + architecture on the current processor. Input is specification of + the control register MCi_CTL for each bank of the MCA architecture. + which controls the generation of machine check exceptions for errors + logged to that bank. + + If MCA is not available on this processor, check if MCE is available. + If so, enable MCE in CR4 + + Arguments: + + Context: Array of values of MCi_CTL for each bank of MCA. + If NULL, use MCA_DEFAULT_BANK_CONF values for each bank + + Return Value: + + None + +--*/ +{ + ULONGLONG MciCtl; + ULONGLONG McgCap; + ULONGLONG McgCtl; + ULONG BankNum; + + + if (HalpFeatureBits & HAL_MCA_PRESENT) { + // + // MCA is available. Initialize MCG_CTL register if present + // Writing all 1's enable MCE or MCA Error Exceptions + // + + McgCap = RDMSR(MCG_CAP); + + if (McgCap & MCG_CTL_PRESENT) { + McgCtl = MCA_MCGCTL_ENABLE_LOGGING; + WRMSR(MCG_CTL, McgCtl); + } + + // + // Enable all MCA errors + // + for ( BankNum = 0; BankNum < HalpMcaInfo.NumBanks; BankNum++ ) { + + // + // Use MCA_DEFAULT_BANK_CONF for each bank + // + + MciCtl = MCA_DEFAULT_BANK_CONF; + + // + // If this is bank 0, use HalpMcaInfo.Bank0Config as a mask + // + if (BankNum == 0) { + MciCtl &= HalpMcaInfo.Bank0Config; + } + + WRMSR(MC0_CTL + (BankNum * MCA_NUM_REGS), MciCtl); + + // + // Clear the MCi_STATUS registers also + // + WRMSR(MC0_STATUS + (BankNum * MCA_NUM_REGS), 0x0); + } + } + + // + // Enable MCE bit in CR4 + // + + HalpSetCr4MCEBit(); +} + + +NTSTATUS +HalpMcaRegisterDriver( + IN PMCA_DRIVER_INFO DriverInfo + ) +/*++ + Routine Description: + This routine is called by the driver (via HalSetSystemInformation) + to register its presence. Only one driver can be registered at a time. + + Arguments: + DriverInfo: Contains info about the callback routine and the DeviceObject + + Return Value: + Unless a MCA driver is already registered OR one of the two callback + routines are NULL, this routine returns Success. +--*/ + +{ + KIRQL OldIrql; + PVOID UnlockHandle; + NTSTATUS Status; + + PAGED_CODE(); + + + Status = STATUS_UNSUCCESSFUL; + + if ((HalpFeatureBits & HAL_MCE_PRESENT) && DriverInfo->DpcCallback) { + + ExAcquireFastMutex (&HalpMcaInfo.Mutex); + + // + // Register driver + // + + if (!HalpMcaInfo.DriverInfo.DpcCallback) { + + // Initialize the DPC object + KeInitializeDpc( + &HalpMcaInfo.Dpc, + DriverInfo->DpcCallback, + DriverInfo->DeviceContext + ); + + // register driver + HalpMcaInfo.DriverInfo.ExceptionCallback = DriverInfo->ExceptionCallback; + HalpMcaInfo.DriverInfo.DpcCallback = DriverInfo->DpcCallback; + HalpMcaInfo.DriverInfo.DeviceContext = DriverInfo->DeviceContext; + Status = STATUS_SUCCESS; + } + + ExReleaseFastMutex (&HalpMcaInfo.Mutex); + } + + return Status; +} + + +NTSTATUS +HalpGetMcaLog ( + OUT PMCA_EXCEPTION Exception, + OUT PULONG ReturnedLength + ) +/*++ + Routine Description: + This is the entry point for driver to read the bank logs + Called by HaliQuerySystemInformation() + + Arguments: + Buffer: into which the error is reported + BufferSize: Size of the passed buffer + Length: of this buffer + + Return Value: + Success or failure + +--*/ +{ + KAFFINITY ActiveProcessors, CurrentAffinity; + PKTHREAD Thread; + NTSTATUS Status; + + PAGED_CODE(); + + if (! (HalpFeatureBits & HAL_MCA_PRESENT)) { + return(STATUS_NO_SUCH_DEVICE); + } + + + Thread = KeGetCurrentThread (); + ActiveProcessors = HalpActiveProcessors; + Status = STATUS_NOT_FOUND; + + ExAcquireFastMutex (&HalpMcaInfo.Mutex); + + for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) { + + if (ActiveProcessors & CurrentAffinity) { + + ActiveProcessors &= ~CurrentAffinity; + KeSetAffinityThread (Thread, CurrentAffinity); + + // + // Check this processor for an exception + // + + Status = HalpMcaReadProcessorException (Exception, FALSE); + + // + // If found, return current information + // + + if (Status != STATUS_NOT_FOUND) { + ASSERT (Status != STATUS_SEVERITY_ERROR); + + *ReturnedLength = sizeof(MCA_EXCEPTION); + break; + } + } + } + + // + // Restore threads affinity, release mutex, and return + // + + KeSetAffinityThread (Thread, HalpActiveProcessors); + ExReleaseFastMutex (&HalpMcaInfo.Mutex); + return Status; +} + + +#if DBG +// +// In checked build, we allocate 4K stack for ourselves and hence there is +// no need to switch to the stack of double fault handler. We can +// directly bugcheck without switching stack. +// +#define HalpMcaSwitchMcaExceptionStackAndBugCheck KeBugCheckEx +#else +NTKERNELAPI +VOID +HalpMcaSwitchMcaExceptionStackAndBugCheck( + IN ULONG BugCheckCode, + IN ULONG BugCheckParameter1, + IN ULONG BugCheckParameter2, + IN ULONG BugCheckParameter3, + IN ULONG BugCheckParameter4 + ); +#endif // DBG + +// Set the following to check async capability + +BOOLEAN NoMCABugCheck = FALSE; + +VOID +HalpMcaExceptionHandler ( + VOID + ) + +/*++ + Routine Description: + This is the MCA exception handler. + + Arguments: + None + + Return Value: + None +--*/ + +{ + NTSTATUS Status; + MCA_EXCEPTION BankLog; + ULONG p1, p2; + ULONGLONG McgStatus, p3; + + BankLog.VersionNumber = 1; + + if (!(HalpFeatureBits & HAL_MCA_PRESENT) ) { + + // + // If we have ONLY MCE (and not MCA), read the MC_ADDR and MC_TYPE + // MSRs, print the values and bugcheck as the errors are not + // restartable. + // + + BankLog.ExceptionType = HAL_MCE_RECORD; + BankLog.u.Mce.Address = RDMSR(PENTIUM_MC_ADDR); + BankLog.u.Mce.Type = RDMSR(PENTIUM_MC_TYPE); + Status = STATUS_SEVERITY_ERROR; + + // + // Parameters for bugcheck + // + + p1 = ((PLARGE_INTEGER)(&BankLog.u.Mce.Type))->LowPart; + p2 = 0; + p3 = BankLog.u.Mce.Address; + + } else { + + McgStatus = RDMSR(MCG_STATUS); + ASSERT( (McgStatus & MCG_MC_INPROGRESS) != 0); + + Status = HalpMcaReadProcessorException (&BankLog, TRUE); + + // + // Clear MCIP bit in MCG_STATUS register + // + + McgStatus = 0; + WRMSR(MCG_STATUS, McgStatus); + + // + // Parameters for bugcheck + // + + p1 = BankLog.u.Mca.BankNumber; + p2 = BankLog.u.Mca.Address.Address; + p3 = BankLog.u.Mca.Status.QuadPart; + } + + if (Status == STATUS_SEVERITY_ERROR) { + + // + // Call the exception callback of the driver so that + // the error can be logged to NVRAM + // + + if (HalpMcaInfo.DriverInfo.ExceptionCallback) { + HalpMcaInfo.DriverInfo.ExceptionCallback ( + HalpMcaInfo.DriverInfo.DeviceContext, + &BankLog + ); + } + + if (!NoMCABugCheck) { + + // + // Bugcheck + // + + HalpMcaSwitchMcaExceptionStackAndBugCheck( + MACHINE_CHECK_EXCEPTION, + p1, + p2, + ((PLARGE_INTEGER)(&p3))->HighPart, + ((PLARGE_INTEGER)(&p3))->LowPart + ); + + // NOT REACHED + } + } + + // + // Must be restartable. Indicate to the timer tick routine that a + // DPC needs queued for MCA driver. + // + + if (HalpMcaInfo.DriverInfo.DpcCallback) { + HalpClockMcaQueueDpc = 1; + } +} + +VOID +HalpMcaQueueDpc( + VOID + ) +/*++ + Routine Description: Gets called from the timer tick to check if DPC + needs to be queued + +--*/ + +{ + KeInsertQueueDpc( + &HalpMcaInfo.Dpc, + NULL, + NULL + ); +} + + +NTSTATUS +HalpMcaReadProcessorException ( + OUT PMCA_EXCEPTION Exception, + IN BOOLEAN NonRestartableOnly + ) +/*++ + + Routine Description: + + This routine logs the errors from the MCA banks on one processor. + Necessary checks for the restartability are performed. The routine + 1> Checks for restartability, and for each bank identifies valid bank + entries and logs error. + 2> If the error is not restartable provides additional information about + bank and the MCA registers. + 3> Resets the Status registers for each bank + + Arguments: + LogExcept: Into which we log the error if found + NonRestartableOnly: Get any error vs. look for error that is not-restartable + + Return Values: + STATUS_SEVERITY_ERROR: Detected non-restartable error. + STATUS_SUCCESS: Successfully logged bank values + STATUS_NOT_FOUND: No error found on any bank + +--*/ +{ + ULONGLONG McgStatus; + UCHAR BankNumber; + MCI_STATS istatus; + NTSTATUS ReturnStatus; + ULONG eflags; + + // + // Read the global status register + // + + McgStatus = RDMSR(MCG_STATUS); + + // + // scan banks on current processor and log contents of first valid bank + // reporting error. Once we find a valid error, no need to read remaining + // banks. It is the application responsibility to read more errors. + // + + ReturnStatus = STATUS_NOT_FOUND; + + for (BankNumber = 0; BankNumber < HalpMcaInfo.NumBanks; BankNumber++) { + + // + // Read the Status MSR of individual bank + // + + istatus.QuadPart = RDMSR(MC0_STATUS + BankNumber * MCA_NUM_REGS); + + + if (istatus.MciStats.Valid == 0) { + // No error in this bank. + continue; + + } + + // + // When MCIP bit is set, the execution can be restarted when + // (MCi_STATUS.DAM == 0) && (MCG_STATUS.RIPV == 1) + // + + if ((McgStatus & MCG_MC_INPROGRESS) && + (!(McgStatus & MCG_RESTART_EIP_VALID) || + istatus.MciStats.Damage)) { + + ReturnStatus = STATUS_SEVERITY_ERROR; + + } else if (NonRestartableOnly == FALSE) { + + ReturnStatus = STATUS_SUCCESS; + + } else { + + // Not the desired type error available here + continue; + } + + // + // Complete exception record + // + + Exception->VersionNumber = 1; + Exception->ExceptionType = HAL_MCA_RECORD; + Exception->TimeStamp.QuadPart = 0; + Exception->u.Mca.Address.QuadPart = 0; + Exception->u.Mca.Misc = 0; + Exception->u.Mca.BankNumber = BankNumber; + Exception->u.Mca.Status = istatus; + + Exception->ProcessorNumber = KeGetCurrentProcessorNumber(); + + if (KeGetCurrentIrql() != CLOCK2_LEVEL) { + KeQuerySystemTime(&Exception->TimeStamp); + } + + if (istatus.MciStats.AddressValid) { + Exception->u.Mca.Address.QuadPart = RDMSR(MC0_ADDR + BankNumber * MCA_NUM_REGS); + } + + if (istatus.MciStats.MiscValid) { + Exception->u.Mca.Misc = RDMSR(MC0_MISC + BankNumber * MCA_NUM_REGS); + } + + if (ReturnStatus != STATUS_SEVERITY_ERROR) { + + // Clear MCi_STATUS register (not a falal error) + + WRMSR(MC0_STATUS + BankNumber * MCA_NUM_REGS, 0); + } + + // + // When the Valid bit of status register is cleared, hardware may write + // a new buffered error report into the error reporting area. The + // serializing instruction is required to permit the update to complete + // + + HalpSerialize (); + + // + // Found entry, done + // + + break; + } + + return(ReturnStatus); +} + +VOID +HalpMcaGetConfiguration ( + OUT PULONG MCEEnabled, + OUT PULONG MCAEnabled +) + +/*++ + +Routine Description: + + This routine stores the Machine Check configuration information. + +Arguments: + + MCEEnabled - Pointer to the MCEEnabled indicator. + 0 = False, 1 = True (0 if value not present in Registry). + + MCAEnabled - Pointer to the MCAEnabled indicator. + 0 = False, 1 = True (1 if value not present in Registry). + +Return Value: + + None. + +--*/ + +{ + + RTL_QUERY_REGISTRY_TABLE Parameters[3]; + ULONG DefaultDataMCE; + ULONG DefaultDataMCA; + + + RtlZeroMemory(Parameters, sizeof(Parameters)); + DefaultDataMCE = *MCEEnabled = FALSE; + DefaultDataMCA = *MCAEnabled = TRUE; + + // + // Gather all of the "user specified" information from + // the registry. + // + + Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[0].Name = rgzEnableMCE; + Parameters[0].EntryContext = MCEEnabled; + Parameters[0].DefaultType = REG_DWORD; + Parameters[0].DefaultData = &DefaultDataMCE; + Parameters[0].DefaultLength = sizeof(ULONG); + + Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT; + Parameters[1].Name = rgzEnableMCA; + Parameters[1].EntryContext = MCAEnabled; + Parameters[1].DefaultType = REG_DWORD; + Parameters[1].DefaultData = &DefaultDataMCA; + Parameters[1].DefaultLength = sizeof(ULONG); + + RtlQueryRegistryValues( + RTL_REGISTRY_CONTROL | RTL_REGISTRY_OPTIONAL, + rgzSessionManager, + Parameters, + NULL, + NULL + ); +} diff --git a/private/ntos/nthals/halx86/i386/ixmcaa.asm b/private/ntos/nthals/halx86/i386/ixmcaa.asm new file mode 100644 index 000000000..229621892 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixmcaa.asm @@ -0,0 +1,420 @@ +;++ +;Module Name +; imca.asm +; +;Abstract: +; Assembly support needed for Intel MCA +; +; Author: +; Anil Aggarwal (Intel Corp) +; +;Revision History: +; +; +;-- + +.586p + .xlist +include hal386.inc +include callconv.inc +include i386\kimacro.inc + .list + + EXTRNP _HalpMcaExceptionHandler,0 + EXTRNP _KeBugCheckEx,5,IMPORT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; DATA Segment +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +_DATA SEGMENT PARA PUBLIC 'DATA' +; +; MCA Exception task stack +; + +MINIMUM_TSS_SIZE EQU TssIoMaps + +if DBG +; +; If we use DbgPrint, we need a larger stack +; +MCA_EXCEPTION_STACK_SIZE EQU 01000H +else +MCA_EXCEPTION_STACK_SIZE EQU 0100H +endif + +KGDT_MCA_TSS EQU 0A0H + + + ; + ; TSS for MCA Exception + ; + align 16 + + public _HalpMcaExceptionTSS +_HalpMcaExceptionTSS label byte + db MINIMUM_TSS_SIZE dup(0) + + ; + ; Stack for MCA exception task + ; + + public _HalpMcaExceptionStack + db MCA_EXCEPTION_STACK_SIZE dup ("*") +_HalpMcaExceptionStack label byte + +_DATA ends + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; TEXT Segment +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +_TEXT SEGMENT PARA PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .586p + +;++ +;ULONGLONG +;FASTCALL +;RDMSR( +; IN ULONG MsrAddress +; ) +; Routine Description: +; This function reads an MSR +; +; Arguments: +; Msr: The address of MSR to be read +; +; Return Value: +; Returns the low 32 bit of MSR in eax and high 32 bits of MSR in edx +; +;-- +cPublicFastCall RDMSR,1 + + rdmsr + fstRET RDMSR + +fstENDP RDMSR + +;++ +; +;VOID +;WRMSR( +; IN ULONG MsrAddress, +; IN ULONGLONG MsrValue +; ) +; Routine Description: +; This function writes an MSR +; +; Arguments: +; Msr: The address of MSR to be written +; Data: The value to be written to the MSR register +; +; Return Value: +; None +; +;-- + +cPublicProc _WRMSR,3 + + mov ecx, [esp + 4] ; MsrAddress + mov eax, [esp + 8] ; Low 32 bits of MsrValue + mov edx, [esp + 12] ; High 32 bits of MsrValue + + wrmsr + stdRET _WRMSR + +stdENDP _WRMSR + +;++ +; +;VOID +;HalpSerialize( +; VOID +; ) +; +; Routine Description: +; This function implements the fence operation for out-of-order execution +; +; Arguments: +; None +; +; Return Value: +; None +; +;-- + +cPublicProc _HalpSerialize,0 + + push ebx + xor eax, eax + cpuid + pop ebx + + stdRET _HalpSerialize + +stdENDP _HalpSerialize + + +;++ +; +; Routine Description: +; +; Machine Check exception handler +; +; +; Arguments: +; +; Return value: +; +; If the error is non-restartable, we will bugcheck. +; Otherwise, we just return +; +;-- + ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING +align dword + public _HalpMcaExceptionHandlerWrapper +_HalpMcaExceptionHandlerWrapper proc +.FPO (0, 0, 0, 0, 0, 2) + + cli + + ; + ; Update the TSS pointer in the PCR to point to the MCA TSS + ; (which is what we're running on, or else we wouldn't be here) + ; + + push dword ptr PCR[PcTss] + mov eax, PCR[PcGdt] + mov ch, [eax+KGDT_MCA_TSS+KgdtBaseHi] + mov cl, [eax+KGDT_MCA_TSS+KgdtBaseMid] + shl ecx, 16 + mov cx, [eax+KGDT_MCA_TSS+KgdtBaseLow] + mov PCR[PcTss], ecx + + ; + ; Clear the busy bit in the TSS selector + ; + mov ecx, PCR[PcGdt] + lea eax, [ecx] + KGDT_MCA_TSS + mov byte ptr [eax+5], 089h ; 32bit, dpl=0, present, TSS32, not busy + + ; + ; Clear Nested Task bit in EFLAGS + ; + pushfd + and [esp], not 04000h + popfd + + ; + ; Check if there is a bugcheck-able error. If need to bugcheck, the + ; caller does it. + ; + stdCall _HalpMcaExceptionHandler + + ; + ; We're back which means that the error was restartable. + ; + + pop dword ptr PCR[PcTss] ; restore PcTss + + mov ecx, PCR[PcGdt] + lea eax, [ecx] + KGDT_TSS + mov byte ptr [eax+5], 08bh ; 32bit, dpl=0, present, TSS32, *busy* + + pushfd ; Set Nested Task bit in EFLAGS + or [esp], 04000h ; so iretd will do a tast switch + popfd + + iretd ; Return from MCA Exception handler + jmp short _HalpMcaExceptionHandlerWrapper + ; For next Machine check exception + +_HalpMcaExceptionHandlerWrapper endp + +;++ +; +; Routine Description: +; +; MCA exception is run off a small stack pointed to by MCA TSS. When +; the error is non-restartable, this routine is called to switch to a larger +; stack which is the overlay of ZW thunks (as is done for double fault stack) +; +; Arguments: +; +; The arguments to KeMachineCheck are passed to this function +; +; Return value: +; +; Never returns. End up doing the bugcheck. +; +;-- + +cPublicProc _HalpMcaSwitchMcaExceptionStackAndBugCheck,5 + + ; Get Task gate descriptor for double fault handler + mov ecx, PCR[PcIdt] ; Get IDT address + lea eax, [ecx] + 040h ; DF Exception is 8 + + ; Get to TSS Descriptor of double fault handler TSS + xor ecx, ecx + mov cx, word ptr [eax+2] + add ecx, PCR[PcGdt] + + ; Get the address of TSS from this TSS Descriptor + mov ah, [ecx+KgdtBaseHi] + mov al, [ecx+KgdtBaseMid] + shl eax, 16 + mov ax, [ecx+KgdtBaseLow] + + ; Get ESP from DF TSS + mov ecx, [eax+038h] + + ; Save the passed arguments before we switch the stacks + mov eax, [esp+4] + mov ebx, [esp+8] + mov edx, [esp+12] + mov esi, [esp+16] + mov edi, [esp+20] + + ; Use the ZW thunk area for the stack to operate on for crash + mov esp, ecx + + stdCall _KeBugCheckEx, + + stdRET _HalpMcaSwitchMcaExceptionStackAndBugCheck + +stdENDP _HalpMcaSwitchMcaExceptionStackAndBugCheck + +_TEXT ends + +INIT SEGMENT DWORD PUBLIC 'CODE' + +;++ +;VOID +;HalpMcaCurrentProcessorSetTSS( +; VOID +; ) +; Routine Description: +; This function sets up the TSS for MCA exception 18 +; +; Arguments: +; Context: We don't care about this but is there since HalpGenericCall +; needs one +; +; Return Value: +; None +; +;-- + +cPublicProc _HalpMcaCurrentProcessorSetTSS,0 + + ; + ; Edit IDT Entry for MCA Exception (18) to contain a task gate + ; + mov ecx, PCR[PcIdt] ; Get IDT address + lea eax, [ecx] + 090h ; MCA Exception is 18 + mov byte ptr [eax + 5], 085h ; P=1,DPL=0,Type=5 + mov word ptr [eax + 2], KGDT_MCA_TSS ; TSS Segment Selector + + mov edx, offset FLAT:_HalpMcaExceptionTSS ; the address of TSS in edx + + ; + ; Set various fields in TSS + ; + mov eax, cr3 + mov [edx + TssCR3], eax + + mov eax, offset FLAT:_HalpMcaExceptionStack; address of MCA Exception stack + mov dword ptr [edx+038h], eax ; Set ESP + mov dword ptr [edx+TssEsp0], eax ; Set ESP0 + + mov dword ptr [edx+020h], offset FLAT:_HalpMcaExceptionHandlerWrapper ; set EIP + mov dword ptr [edx+024h], 0 ; set EFLAGS + mov word ptr [edx+04ch],KGDT_R0_CODE ; set value for CS + mov word ptr [edx+058h],KGDT_R0_PCR ; set value for FS + mov [edx+050h], ss + mov word ptr [edx+048h],KGDT_R3_DATA OR RPL_MASK ; Es + mov word ptr [edx+054h],KGDT_R3_DATA OR RPL_MASK ; Ds + + ; + ; Part that gets done in KiInitialiazeTSS() + ; + mov word ptr [edx + 08], KGDT_R0_DATA ; Set SS0 + mov word ptr [edx + 060h],0 ; Set LDT + mov word ptr [edx + 064h],0 ; Set T bit + mov word ptr [edx + 066h],020adh ; I/O Map base address = sizeof(KTSS)+1 + + ; + ; Edit GDT entry for KGDT_MCA_TSS to create a valid TSS Descriptor + ; + mov ecx, PCR[PcGdt] ; Get GDT address + lea eax, [ecx] + KGDT_MCA_TSS ; offset of MCA TSS in GDT + mov ecx, eax + + ; + ; Set Type field of TSS Descriptor + ; + mov byte ptr [ecx + 5], 089H ; P=1, DPL=0, Type = 9 + + ; + ; Set Base Address field of TSS Descriptor + ; + mov eax, edx ; TSS address in eax + mov [ecx + KgdtBaseLow], ax + shr eax, 16 + mov [ecx + KgdtBaseHi],ah + mov [ecx + KgdtBaseMid],al + + ; + ; Set Segment limit for TSS Descriptor + ; + mov eax, MINIMUM_TSS_SIZE + mov [ecx + KgdtLimitLow],ax + + stdRET _HalpMcaCurrentProcessorSetTSS + +stdENDP _HalpMcaCurrentProcessorSetTSS + + +;++ +; +;VOID +;HalpSetCr4MCEBit( +; VOID +; ) +; +; Routine Description: +; This function sets the CR4.MCE bit +; +; Arguments: +; None +; +; Return Value: +; None +; +;-- + +cPublicProc _HalpSetCr4MCEBit,0 + + mov eax, cr4 + or eax, CR4_MCE + mov cr4, eax + stdRET _HalpSetCr4MCEBit + +stdENDP _HalpSetCr4MCEBit + + +INIT ends + + end + diff --git a/private/ntos/nthals/halx86/i386/ixnmi.c b/private/ntos/nthals/halx86/i386/ixnmi.c new file mode 100644 index 000000000..e00035bd8 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixnmi.c @@ -0,0 +1,137 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ixnmi.c + +Abstract: + + Provides standard x86 NMI handler + +Author: + + kenr + +Revision History: + +--*/ +#include "halp.h" +#include "bugcodes.h" + +#define SYSTEM_CONTROL_PORT_A 0x92 +#define SYSTEM_CONTROL_PORT_B 0x61 +#define EISA_EXTENDED_NMI_STATUS 0x461 + +UCHAR EisaNMIMsg[] = MSG_NMI_EISA_IOCHKERR; + + +VOID +HalHandleNMI( + IN OUT PVOID NmiInfo + ) +/*++ + +Routine Description: + + Called DURING an NMI. The system will BugCheck when an NMI occurs. + This function can return the proper bugcheck code, bugcheck itself, + or return success which will cause the system to iret from the nmi. + + This function is called during an NMI - no system services are available. + In addition, you don't want to touch any spinlock which is normally + used since we may have been interrupted while owning it, etc, etc... + +Warnings: + + Do NOT: + Make any system calls + Attempt to acquire any spinlock used by any code outside the NMI handler + Change the interrupt state. Do not execute any IRET inside this code + + Passing data to non-NMI code must be done using manual interlocked + functions. (xchg instructions). + +Arguments: + + NmiInfo - Pointer to NMI information structure (TBD) + - NULL means no NMI information structure was passed + +Return Value: + + BugCheck code + +--*/ +{ + UCHAR StatusByte; + UCHAR EisaPort; + UCHAR c; + ULONG port, i; + + HalDisplayString (MSG_HARDWARE_ERROR1); + HalDisplayString (MSG_HARDWARE_ERROR2); + + StatusByte = READ_PORT_UCHAR((PUCHAR) SYSTEM_CONTROL_PORT_B); + + if (StatusByte & 0x80) { + HalDisplayString (MSG_NMI_PARITY); + } + + if (StatusByte & 0x40) { + HalDisplayString (MSG_NMI_CHANNEL_CHECK); + } + + if (HalpBusType == MACHINE_TYPE_EISA) { + // + // This is an Eisa machine, check for extnded nmi information... + // + + StatusByte = READ_PORT_UCHAR((PUCHAR) EISA_EXTENDED_NMI_STATUS); + + if (StatusByte & 0x80) { + HalDisplayString (MSG_NMI_FAIL_SAFE); + } + + if (StatusByte & 0x40) { + HalDisplayString (MSG_NMI_BUS_TIMEOUT); + } + + if (StatusByte & 0x20) { + HalDisplayString (MSG_NMI_SOFTWARE_NMI); + } + + // + // Look for any Eisa expansion board. See if it asserted NMI. + // + + for (EisaPort = 1; EisaPort <= 0xf; EisaPort++) { + port = (EisaPort << 12) + 0xC80; + WRITE_PORT_UCHAR ((PUCHAR) port, 0xff); + StatusByte = READ_PORT_UCHAR ((PUCHAR) port); + + if ((StatusByte & 0x80) == 0) { + // + // Found valid Eisa board, Check to see if it's + // if IOCHKERR is asserted. + // + + StatusByte = READ_PORT_UCHAR ((PUCHAR) port+4); + if (StatusByte & 0x2 && StatusByte != 0xff) { + c = (EisaPort > 9 ? 'A'-10 : '0') + EisaPort; + for (i=0; EisaNMIMsg[i]; i++) { + if (EisaNMIMsg[i] == '%') { + EisaNMIMsg[i] = c; + HalDisplayString (EisaNMIMsg); + EisaNMIMsg[i] = '%'; + break; + } + } + } + } + } + } + + HalDisplayString (MSG_HALT); + KeEnterKernelDebugger(); +} diff --git a/private/ntos/nthals/halx86/i386/ixpcibrd.c b/private/ntos/nthals/halx86/i386/ixpcibrd.c new file mode 100644 index 000000000..c8b87d31e --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixpcibrd.c @@ -0,0 +1,1029 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixpcibrd.c + +Abstract: + + Get PCI-PCI bridge information + +Author: + + Ken Reneris (kenr) 14-June-1994 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "halp.h" +#include "pci.h" +#include "pcip.h" +#include "stdio.h" + +// debugging only... +// #define INIT_PCI_BRIDGE 1 + +extern WCHAR rgzMultiFunctionAdapter[]; +extern WCHAR rgzConfigurationData[]; +extern WCHAR rgzIdentifier[]; +extern WCHAR rgzReservedResources[]; + + +#if DBG +#define DBGMSG(a) DbgPrint(a) +#else +#define DBGMSG(a) +#endif + + + +#define IsPciBridge(a) \ + (a->VendorID != PCI_INVALID_VENDORID && \ + PCI_CONFIG_TYPE(a) == PCI_BRIDGE_TYPE && \ + a->SubClass == 4 && a->BaseClass == 6) + + +typedef struct { + ULONG BusNo; + PBUS_HANDLER BusHandler; + PPCIPBUSDATA BusData; + PCI_SLOT_NUMBER SlotNumber; + PPCI_COMMON_CONFIG PciData; + ULONG IO, Memory, PFMemory; + UCHAR Buffer[PCI_COMMON_HDR_LENGTH]; +} CONFIGBRIDGE, *PCONFIGBRIDGE; + +// +// Internal prototypes +// + + +#ifdef INIT_PCI_BRIDGE +VOID +HalpGetPciBridgeNeeds ( + IN ULONG HwType, + IN PUCHAR MaxPciBus, + IN PCONFIGBRIDGE Current + ); +#endif + +VOID +HalpSetPciBridgedVgaCronk ( + IN ULONG BusNumber, + IN ULONG Base, + IN ULONG Limit + ); + + +ULONG +HalpGetBridgedPCIInterrupt ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +ULONG +HalpGetBridgedPCIISAInt ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +VOID +HalpPCIBridgedPin2Line ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciData + ); + + +VOID +HalpPCIBridgedLine2Pin ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciNewData, + IN PPCI_COMMON_CONFIG PciOldData + ); + +NTSTATUS +HalpGetBridgedPCIIrqTable ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER PciSlot, + OUT PUCHAR IrqTable + ); + + + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,HalpGetPciBridgeConfig) +#pragma alloc_text(INIT,HalpSetPciBridgedVgaCronk) +#pragma alloc_text(INIT,HalpFixupPciSupportedRanges) + +#ifdef INIT_PCI_BRIDGE +#pragma alloc_text(PAGE,HalpGetBridgedPCIInterrupt) +//#pragma alloc_text(PAGE,HalpGetBridgedPCIIrqTable) +#pragma alloc_text(INIT,HalpGetPciBridgeNeeds) +#endif +#endif + + +BOOLEAN +HalpGetPciBridgeConfig ( + IN ULONG HwType, + IN PUCHAR MaxPciBus + ) +/*++ + +Routine Description: + + Scan the devices on all known pci buses trying to locate any + pci to pci bridges. Record the hierarchy for the buses, and + which buses have what addressing limits. + +Arguments: + + HwType - Configuration type. + MaxPciBus - # of PCI buses reported by the bios + +--*/ +{ + PBUS_HANDLER ChildBus; + PPCIPBUSDATA ChildBusData; + ULONG d, f, i, j, BusNo; + UCHAR Rescan; + BOOLEAN FoundDisabledBridge; + CONFIGBRIDGE CB; + + Rescan = 0; + FoundDisabledBridge = FALSE; + + // + // Find each bus on a bridge and initialize it's base and limit information + // + + CB.PciData = (PPCI_COMMON_CONFIG) CB.Buffer; + CB.SlotNumber.u.bits.Reserved = 0; + for (BusNo=0; BusNo < *MaxPciBus; BusNo++) { + + CB.BusHandler = HalpHandlerForBus (PCIBus, BusNo); + CB.BusData = (PPCIPBUSDATA) CB.BusHandler->BusData; + + for (d = 0; d < PCI_MAX_DEVICES; d++) { + CB.SlotNumber.u.bits.DeviceNumber = d; + + for (f = 0; f < PCI_MAX_FUNCTION; f++) { + CB.SlotNumber.u.bits.FunctionNumber = f; + + // + // Read PCI configuration information + // + + HalpReadPCIConfig ( + CB.BusHandler, + CB.SlotNumber, + CB.PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + if (CB.PciData->VendorID == PCI_INVALID_VENDORID) { + // next device + break; + } + + if (!IsPciBridge (CB.PciData)) { + // not a PCI-PCI bridge, next function + continue; + } + + if (!(CB.PciData->Command & PCI_ENABLE_BUS_MASTER)) { + // this PCI bridge is not enabled - skip it for now + FoundDisabledBridge = TRUE; + continue; + } + + if ((ULONG) CB.PciData->u.type1.PrimaryBus != + CB.BusHandler->BusNumber) { + + DBGMSG ("HAL GetPciData: bad primarybus!!!\n"); + // skip it... + continue; + } + + if ((ULONG) CB.PciData->u.type1.SecondaryBus <= CB.BusHandler->BusNumber) { + + // secondary bus number doesn't make any sense. HP Omnibook may + // not fill this field in on a virtually disabled pci-pci bridge + + FoundDisabledBridge = TRUE; + continue; + } + + // + // Found a PCI-PCI bridge. Determine it's parent child + // releationships + // + + ChildBus = HalpHandlerForBus (PCIBus, CB.PciData->u.type1.SecondaryBus); + if (!ChildBus) { + DBGMSG ("HAL GetPciData: found configured pci bridge\n"); + + // up the number of buses + if (CB.PciData->u.type1.SecondaryBus > Rescan) { + Rescan = CB.PciData->u.type1.SecondaryBus; + } + continue; + } + + ChildBusData = (PPCIPBUSDATA) ChildBus->BusData; + if (ChildBusData->BridgeConfigRead) { + // this child buses releationships already processed + continue; + } + + // + // Remember the limits which are programmed into this bridge + // + + ChildBusData->BridgeConfigRead = TRUE; + HalpSetBusHandlerParent (ChildBus, CB.BusHandler); + ChildBusData->ParentBus = (UCHAR) CB.BusHandler->BusNumber; + ChildBusData->CommonData.ParentSlot = CB.SlotNumber; + + ChildBus->BusAddresses->IO.Base = + PciBridgeIO2Base( + CB.PciData->u.type1.IOBase, + CB.PciData->u.type1.IOBaseUpper16 + ); + + ChildBus->BusAddresses->IO.Limit = + PciBridgeIO2Limit( + CB.PciData->u.type1.IOLimit, + CB.PciData->u.type1.IOLimitUpper16 + ); + + // + // Special VGA address remapping occuring on this bridge? + // + + if (CB.PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA && + ChildBus->BusAddresses->IO.Base < ChildBus->BusAddresses->IO.Limit) { + + HalpSetPciBridgedVgaCronk ( + ChildBus->BusNumber, + (ULONG) ChildBus->BusAddresses->IO.Base, + (ULONG) ChildBus->BusAddresses->IO.Limit + ); + } + + // + // If supported I/O ranges on this bus are limitied to + // 256bytes on every 1K aligned boundry within the + // range, then redo supported IO BusAddresses to match + // + + if (CB.PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_ISA && + ChildBus->BusAddresses->IO.Base < ChildBus->BusAddresses->IO.Limit) { + + // assume Base is 1K aligned + i = (ULONG) ChildBus->BusAddresses->IO.Base; + j = (ULONG) ChildBus->BusAddresses->IO.Limit; + + // convert head entry + ChildBus->BusAddresses->IO.Limit = i + 255; + i += 1024; + + // add remaining ranges + while (i < j) { + HalpAddRange ( + &ChildBus->BusAddresses->IO, + 1, // address space + 0, // system base + i, // bus address + i + 255 // bus limit + ); + + // next range + i += 1024; + } + } + + ChildBus->BusAddresses->Memory.Base = + PciBridgeMemory2Base(CB.PciData->u.type1.MemoryBase); + + ChildBus->BusAddresses->Memory.Limit = + PciBridgeMemory2Limit(CB.PciData->u.type1.MemoryLimit); + + // On x86 it's ok to clip Prefetch to 32 bits + + if (CB.PciData->u.type1.PrefetchBaseUpper32 == 0) { + ChildBus->BusAddresses->PrefetchMemory.Base = + PciBridgeMemory2Base(CB.PciData->u.type1.PrefetchBase); + + + ChildBus->BusAddresses->PrefetchMemory.Limit = + PciBridgeMemory2Limit(CB.PciData->u.type1.PrefetchLimit); + + if (CB.PciData->u.type1.PrefetchLimitUpper32) { + ChildBus->BusAddresses->PrefetchMemory.Limit = 0xffffffff; + } + } + + // should call HalpAssignPCISlotResources to assign + // baseaddresses, etc... + } + } + } + + if (Rescan) { + *MaxPciBus = Rescan+1; + return TRUE; + } + + if (!FoundDisabledBridge) { + return FALSE; + } + + DBGMSG ("HAL GetPciData: found disabled pci bridge\n"); + +#ifdef INIT_PCI_BRIDGE + // + // We've calculated all the parent's buses known bases & limits. + // While doing this a pci-pci bus was found that the bios didn't + // configure. This is not expected, and we'll make some guesses + // at a configuration here and enable it. + // + // (this code is primarily for testing the above code since + // currently no system bioses actually configure the child buses) + // + + for (BusNo=0; BusNo < *MaxPciBus; BusNo++) { + + CB.BusHandler = HalpHandlerForBus (PCIBus, BusNo); + CB.BusData = (PPCIPBUSDATA) CB.BusHandler->BusData; + + for (d = 0; d < PCI_MAX_DEVICES; d++) { + CB.SlotNumber.u.bits.DeviceNumber = d; + + for (f = 0; f < PCI_MAX_FUNCTION; f++) { + CB.SlotNumber.u.bits.FunctionNumber = f; + + HalpReadPCIConfig ( + CB.BusHandler, + CB.SlotNumber, + CB.PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + if (CB.PciData->VendorID == PCI_INVALID_VENDORID) { + break; + } + + if (!IsPciBridge (CB.PciData)) { + // not a PCI-PCI bridge + continue; + } + + if ((CB.PciData->Command & PCI_ENABLE_BUS_MASTER)) { + // this PCI bridge is enabled + continue; + } + + // + // We have a disabled bus - assign it a number, then + // determine all the requirements of all devices + // on the other side of this bridge + // + + CB.BusNo = BusNo; + HalpGetPciBridgeNeeds (HwType, MaxPciBus, &CB); + } + } + } + // preform Rescan + return TRUE; + +#else + + return FALSE; + +#endif + +} + +VOID +HalpFixupPciSupportedRanges ( + IN ULONG MaxBuses + ) +/*++ + +Routine Description: + + PCI-PCI bridged buses only see addresses which their parent + bueses support. So adjust any PCI SUPPORT_RANGES to be + a complete subset of all of it's parent buses. + + PCI-PCI briges use postive address decode to forward addresses. + So, remove any addresses from any PCI bus which are bridged to + a child PCI bus. + +--*/ +{ + ULONG i; + PBUS_HANDLER Bus, ParentBus; + PSUPPORTED_RANGES HRanges; + + // + // Pass 1 - shrink all PCI supported ranges to be a subset of + // all of it's parent buses + // + + for (i = 0; i < MaxBuses; i++) { + + Bus = HalpHandlerForBus (PCIBus, i); + + ParentBus = Bus->ParentHandler; + while (ParentBus) { + + HRanges = Bus->BusAddresses; + Bus->BusAddresses = HalpMergeRanges ( + ParentBus->BusAddresses, + HRanges + ); + + HalpFreeRangeList (HRanges); + ParentBus = ParentBus->ParentHandler; + } + } + + // + // Pass 2 - remove all child PCI bus ranges from parent PCI buses + // + + for (i = 0; i < MaxBuses; i++) { + Bus = HalpHandlerForBus (PCIBus, i); + + ParentBus = Bus->ParentHandler; + while (ParentBus) { + + if (ParentBus->InterfaceType == PCIBus) { + HalpRemoveRanges ( + ParentBus->BusAddresses, + Bus->BusAddresses + ); + } + + ParentBus = ParentBus->ParentHandler; + } + } + + // + // Cleanup + // + + for (i = 0; i < MaxBuses; i++) { + Bus = HalpHandlerForBus (PCIBus, i); + HalpConsolidateRanges (Bus->BusAddresses); + } +} + + + +VOID +HalpSetPciBridgedVgaCronk ( + IN ULONG BusNumber, + IN ULONG BaseAddress, + IN ULONG LimitAddress + ) +/*++ + +Routine Description: . + + The 'vga compatible addresses' bit is set in the bridge control regiter. + This causes the bridge to pass any I/O address in the range of: 10bit + decode 3b0-3bb & 3c0-3df, as TEN bit addresses. + + As far as I can tell this "feature" is an attempt to solve some problem + which the folks solving it did not fully understand, so instead of doing + it right we have this fine mess. + + The solution is to take the least of all evils which is to remove any + I/O port ranges which are getting remapped from any IoAssignResource + request. (ie, IoAssignResources will never contimplate giving any + I/O port out in the suspected ranges). + + note: memory allocation error here is fatal so don't bother with the + return codes. + +Arguments: + + Base - Base of IO address range in question + Limit - Limit of IO address range in question + +--*/ +{ + UNICODE_STRING unicodeString; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE handle; + ULONG Length; + PCM_RESOURCE_LIST ResourceList; + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + ULONG AddressMSBs; + WCHAR ValueName[80]; + NTSTATUS status; + + // + // Open reserved resource settings + // + + RtlInitUnicodeString (&unicodeString, rgzReservedResources); + InitializeObjectAttributes( &objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + NULL, + (PSECURITY_DESCRIPTOR) NULL + ); + + status = ZwOpenKey( &handle, KEY_READ|KEY_WRITE, &objectAttributes); + if (!NT_SUCCESS(status)) { + return; + } + + // + // Build resource list of reseved ranges + // + + Length = ((LimitAddress - BaseAddress) / 1024 + 2) * 2 * + sizeof (CM_PARTIAL_RESOURCE_DESCRIPTOR) + + sizeof (CM_RESOURCE_LIST); + + ResourceList = (PCM_RESOURCE_LIST) ExAllocatePool (PagedPool, Length); + memset (ResourceList, 0, Length); + + ResourceList->Count = 1; + ResourceList->List[0].InterfaceType = PCIBus; + ResourceList->List[0].BusNumber = BusNumber; + Descriptor = ResourceList->List[0].PartialResourceList.PartialDescriptors; + + while (BaseAddress < LimitAddress) { + AddressMSBs = BaseAddress & ~0x3ff; // get upper 10bits of addr + + // + // Add xx3b0 through xx3bb + // + + Descriptor->Type = CmResourceTypePort; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Flags = CM_RESOURCE_PORT_IO; + Descriptor->u.Port.Start.QuadPart = AddressMSBs | 0x3b0; + Descriptor->u.Port.Length = 0xb; + + Descriptor += 1; + ResourceList->List[0].PartialResourceList.Count += 1; + + // + // Add xx3c0 through xx3df + // + + Descriptor->Type = CmResourceTypePort; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Flags = CM_RESOURCE_PORT_IO; + Descriptor->u.Port.Start.QuadPart = AddressMSBs | 0x3c0; + Descriptor->u.Port.Length = 0x1f; + + Descriptor += 1; + ResourceList->List[0].PartialResourceList.Count += 1; + + // + // Next range + // + + BaseAddress += 1024; + } + + // + // Add the reserved ranges to avoid during IoAssignResource + // + + swprintf (ValueName, L"HAL_PCI_%d", BusNumber); + RtlInitUnicodeString (&unicodeString, ValueName); + + ZwSetValueKey (handle, + &unicodeString, + 0L, + REG_RESOURCE_LIST, + ResourceList, + (ULONG) Descriptor - (ULONG) ResourceList + ); + + + ExFreePool (ResourceList); + ZwClose (handle); +} + + + +#ifdef INIT_PCI_BRIDGE + +VOID +HalpGetPciBridgeNeeds ( + IN ULONG HwType, + IN PUCHAR MaxPciBus, + IN PCONFIGBRIDGE Current + ) +{ + ACCESS_MASK DesiredAccess; + UNICODE_STRING unicodeString; + PUCHAR buffer; + HANDLE handle; + OBJECT_ATTRIBUTES objectAttributes; + PCM_FULL_RESOURCE_DESCRIPTOR Descriptor; + PCONFIGURATION_COMPONENT Component; + CONFIGBRIDGE CB; + ULONG mnum, d, f, i; + NTSTATUS status; + + buffer = ExAllocatePool (PagedPool, 1024); + + // init + CB.PciData = (PPCI_COMMON_CONFIG) CB.Buffer; + CB.SlotNumber.u.bits.Reserved = 0; + Current->IO = Current->Memory = Current->PFMemory = 0; + + // + // Assign this bridge an ID, and turn on configuration space + // + + Current->PciData->u.type1.PrimaryBus = (UCHAR) Current->BusNo; + Current->PciData->u.type1.SecondaryBus = (UCHAR) *MaxPciBus; + Current->PciData->u.type1.SubordinateBus = (UCHAR) 0xFF; + Current->PciData->u.type1.SecondaryStatus = 0xffff; + Current->PciData->Status = 0xffff; + Current->PciData->Command = 0; + + Current->PciData->u.type1.BridgeControl = PCI_ASSERT_BRIDGE_RESET; + + HalpWritePCIConfig ( + Current->BusHandler, + Current->SlotNumber, + Current->PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + KeStallExecutionProcessor (100); + + Current->PciData->u.type1.BridgeControl = 0; + HalpWritePCIConfig ( + Current->BusHandler, + Current->SlotNumber, + Current->PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + + KeStallExecutionProcessor (100); + + // + // Allocate new handler for bus + // + + CB.BusHandler = HalpAllocateAndInitPciBusHandler (HwType, *MaxPciBus, FALSE); + CB.BusData = (PPCIPBUSDATA) CB.BusHandler->BusData; + CB.BusNo = *MaxPciBus; + *MaxPciBus += 1; + + // + // Add another PCI bus in the registry + // + + mnum = 0; + for (; ;) { + // + // Find next available MultiFunctionAdapter key + // + + DesiredAccess = KEY_READ | KEY_WRITE; + swprintf ((PWCHAR) buffer, L"%s\\%d", rgzMultiFunctionAdapter, mnum); + RtlInitUnicodeString (&unicodeString, (PWCHAR) buffer); + + InitializeObjectAttributes( &objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + NULL, + (PSECURITY_DESCRIPTOR) NULL + ); + + status = ZwOpenKey( &handle, DesiredAccess, &objectAttributes); + if (!NT_SUCCESS(status)) { + break; + } + + // already exists, next + ZwClose (handle); + mnum += 1; + } + + ZwCreateKey (&handle, + DesiredAccess, + &objectAttributes, + 0, + NULL, + REG_OPTION_VOLATILE, + &d + ); + + // + // Add needed registry values for this MultifucntionAdapter entry + // + + RtlInitUnicodeString (&unicodeString, rgzIdentifier); + ZwSetValueKey (handle, + &unicodeString, + 0L, + REG_SZ, + L"PCI", + sizeof (L"PCI") + ); + + RtlInitUnicodeString (&unicodeString, rgzConfigurationData); + Descriptor = (PCM_FULL_RESOURCE_DESCRIPTOR) buffer; + Descriptor->InterfaceType = PCIBus; + Descriptor->BusNumber = CB.BusNo; + Descriptor->PartialResourceList.Version = 0; + Descriptor->PartialResourceList.Revision = 0; + Descriptor->PartialResourceList.Count = 0; + ZwSetValueKey (handle, + &unicodeString, + 0L, + REG_FULL_RESOURCE_DESCRIPTOR, + Descriptor, + sizeof (*Descriptor) + ); + + + RtlInitUnicodeString (&unicodeString, L"Component Information"); + Component = (PCONFIGURATION_COMPONENT) buffer; + RtlZeroMemory (Component, sizeof (*Component)); + Component->AffinityMask = 0xffffffff; + ZwSetValueKey (handle, + &unicodeString, + 0L, + REG_BINARY, + Component, + FIELD_OFFSET (CONFIGURATION_COMPONENT, ConfigurationDataLength) + ); + + ZwClose (handle); + + + // + // Since the BIOS didn't configure this bridge we'll assume that + // the PCI interrupts are bridged. (for BIOS configured buses we + // assume that the BIOS put the ISA bus IRQ in the InterruptLine value) + // + + CB.BusData->Pin2Line = (PciPin2Line) HalpPCIBridgedPin2Line; + CB.BusData->Line2Pin = (PciLine2Pin) HalpPCIBridgedLine2Pin; + //CB.BusData->GetIrqTable = (PciIrqTable) HalpGetBridgedPCIIrqTable; + + if (Current->BusHandler->GetInterruptVector == HalpGetPCIIntOnISABus) { + + // + // The parent bus'es interrupt pin to vector mappings is not + // a static function, and is determined by the boot firmware. + // + + //CB.BusHandler->GetInterruptVector = (PGETINTERRUPTVECTOR) HalpGetBridgedPCIISAInt; + + // read each device on parent bus + for (d = 0; d < PCI_MAX_DEVICES; d++) { + CB.SlotNumber.u.bits.DeviceNumber = d; + + for (f = 0; f < PCI_MAX_FUNCTION; f++) { + CB.SlotNumber.u.bits.FunctionNumber = f; + + HalpReadPCIConfig ( + Current->BusHandler, + CB.SlotNumber, + CB.PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + if (CB.PciData->VendorID == PCI_INVALID_VENDORID) { + break; + } + + if (CB.PciData->u.type0.InterruptPin && + (PCI_CONFIG_TYPE (CB.PciData) == PCI_DEVICE_TYPE || + PCI_CONFIG_TYPE (CB.PciData) == PCI_BRIDGE_TYPE)) { + + // get bios supplied int mapping + i = CB.PciData->u.type0.InterruptPin + d % 4; + CB.BusData->SwizzleIn[i] = CB.PciData->u.type0.InterruptLine; + } + } + } + + } else { + _asm int 3; + } + + // + // Look at each device on the bus and determine it's resource needs + // + + for (d = 0; d < PCI_MAX_DEVICES; d++) { + CB.SlotNumber.u.bits.DeviceNumber = d; + + for (f = 0; f < PCI_MAX_FUNCTION; f++) { + CB.SlotNumber.u.bits.FunctionNumber = f; + + HalpReadPCIConfig ( + CB.BusHandler, + CB.SlotNumber, + CB.PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + if (CB.PciData->VendorID == PCI_INVALID_VENDORID) { + break; + } + + if (IsPciBridge (CB.PciData)) { + // oh look - another bridge ... + HalpGetPciBridgeNeeds (HwType, MaxPciBus, &CB); + continue; + } + + if (PCI_CONFIG_TYPE (CB.PciData) != PCI_DEVICE_TYPE) { + continue; + } + + // found a device - figure out the resources it needs + } + } + + // + // Found all sub-buses set SubordinateBus accordingly + // + + Current->PciData->u.type1.SubordinateBus = (UCHAR) *MaxPciBus - 1; + + HalpWritePCIConfig ( + Current->BusHandler, + Current->SlotNumber, + Current->PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + + // + // Set the bridges IO, Memory, and Prefetch Memory windows + // + + // For now just pick some numbers & set everyone the same + // IO 0x6000 - 0xFFFF + // MEM 0x40000000 - 0x4FFFFFFF + // PFMEM 0x50000000 - 0x5FFFFFFF + + Current->PciData->u.type1.IOBase = 0x6000 >> 12 << 4; + Current->PciData->u.type1.IOLimit = 0xffff >> 12 << 4; + Current->PciData->u.type1.MemoryBase = 0x40000000 >> 20 << 4; + Current->PciData->u.type1.MemoryLimit = 0x4fffffff >> 20 << 4; + Current->PciData->u.type1.PrefetchBase = 0x50000000 >> 20 << 4; + Current->PciData->u.type1.PrefetchLimit = 0x5fffffff >> 20 << 4; + + Current->PciData->u.type1.PrefetchBaseUpper32 = 0; + Current->PciData->u.type1.PrefetchLimitUpper32 = 0; + Current->PciData->u.type1.IOBaseUpper16 = 0; + Current->PciData->u.type1.IOLimitUpper16 = 0; + Current->PciData->u.type1.BridgeControl = + PCI_ENABLE_BRIDGE_ISA; + + HalpWritePCIConfig ( + Current->BusHandler, + Current->SlotNumber, + Current->PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + HalpReadPCIConfig ( + Current->BusHandler, + Current->SlotNumber, + Current->PciData, + 0, + PCI_COMMON_HDR_LENGTH + ); + + // enable memory & io decodes + + Current->PciData->Command = + PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE | PCI_ENABLE_BUS_MASTER; + + HalpWritePCIConfig ( + Current->BusHandler, + Current->SlotNumber, + &Current->PciData->Command, + FIELD_OFFSET (PCI_COMMON_CONFIG, Command), + sizeof (Current->PciData->Command) + ); + + ExFreePool (buffer); +} + +VOID +HalpPCIBridgedPin2Line ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciData + ) +/*++ + + This function maps the device's InterruptPin to an InterruptLine + value. + + test function particular to dec pci-pci bridge card + +--*/ +{ + PPCIPBUSDATA BusData; + ULONG i; + + if (!PciData->u.type0.InterruptPin) { + return ; + } + + BusData = (PPCIPBUSDATA) BusHandler->BusData; + + // + // Convert slot Pin into Bus INTA-D. + // + + i = (PciData->u.type0.InterruptPin + + SlotNumber.u.bits.DeviceNumber - 1) % 4; + + PciData->u.type0.InterruptLine = BusData->SwizzleIn[i] ^ IRQXOR; + PciData->u.type0.InterruptLine = 0x0b ^ IRQXOR; +} + + +VOID +HalpPCIBridgedLine2Pin ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciNewData, + IN PPCI_COMMON_CONFIG PciOldData + ) +/*++ + + This functions maps the device's InterruptLine to it's + device specific InterruptPin value. + + test function particular to dec pci-pci bridge card + +--*/ +{ + PPCIPBUSDATA BusData; + ULONG i; + + if (!PciNewData->u.type0.InterruptPin) { + return ; + } + + BusData = (PPCIPBUSDATA) BusHandler->BusData; + + i = (PciNewData->u.type0.InterruptPin + + SlotNumber.u.bits.DeviceNumber - 1) % 4; + + PciNewData->u.type0.InterruptLine = BusData->SwizzleIn[i] ^ IRQXOR; + PciNewData->u.type0.InterruptLine = 0x0b ^ IRQXOR; +} + +#endif diff --git a/private/ntos/nthals/halx86/i386/ixpcibus.c b/private/ntos/nthals/halx86/i386/ixpcibus.c new file mode 100644 index 000000000..7565c964b --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixpcibus.c @@ -0,0 +1,2520 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixpcidat.c + +Abstract: + + Get/Set bus data routines for the PCI bus + +Author: + + Ken Reneris (kenr) 14-June-1994 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "halp.h" +#include "pci.h" +#include "pcip.h" + +extern WCHAR rgzMultiFunctionAdapter[]; +extern WCHAR rgzConfigurationData[]; +extern WCHAR rgzIdentifier[]; +extern WCHAR rgzPCIIdentifier[]; + + +typedef ULONG (*FncConfigIO) ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +typedef VOID (*FncSync) ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PKIRQL Irql, + IN PVOID State + ); + +typedef VOID (*FncReleaseSync) ( + IN PBUS_HANDLER BusHandler, + IN KIRQL Irql + ); + +typedef struct _PCI_CONFIG_HANDLER { + FncSync Synchronize; + FncReleaseSync ReleaseSynchronzation; + FncConfigIO ConfigRead[3]; + FncConfigIO ConfigWrite[3]; +} PCI_CONFIG_HANDLER, *PPCI_CONFIG_HANDLER; + + + +// +// Prototypes +// + +ULONG +HalpGetPCIData ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +ULONG +HalpSetPCIData ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +NTSTATUS +HalpAssignPCISlotResources ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PUNICODE_STRING RegistryPath, + IN PUNICODE_STRING DriverClassName OPTIONAL, + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT DeviceObject OPTIONAL, + IN ULONG SlotNumber, + IN OUT PCM_RESOURCE_LIST *AllocatedResources + ); + +VOID +HalpInitializePciBus ( + VOID + ); + +BOOLEAN +HalpIsValidPCIDevice ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot + ); + +BOOLEAN +HalpValidPCISlot ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot + ); + +//------------------------------------------------- + +VOID HalpPCISynchronizeType1 ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PKIRQL Irql, + IN PVOID State + ); + +VOID HalpPCIReleaseSynchronzationType1 ( + IN PBUS_HANDLER BusHandler, + IN KIRQL Irql + ); + +VOID +HalpPCISynchronizeOrionB0 ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PKIRQL Irql, + IN PPCI_TYPE1_CFG_BITS PciCfg1 + ); + +VOID +HalpPCIReleaseSynchronzationOrionB0 ( + IN PBUS_HANDLER BusHandler, + IN KIRQL Irql + ); + +ULONG HalpPCIReadUlongType1 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIReadUcharType1 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIReadUshortType1 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIWriteUlongType1 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIWriteUcharType1 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIWriteUshortType1 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +VOID HalpPCISynchronizeType2 ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PKIRQL Irql, + IN PVOID State + ); + +VOID HalpPCIReleaseSynchronzationType2 ( + IN PBUS_HANDLER BusHandler, + IN KIRQL Irql + ); + +ULONG HalpPCIReadUlongType2 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIReadUcharType2 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIReadUshortType2 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIWriteUlongType2 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIWriteUcharType2 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + +ULONG HalpPCIWriteUshortType2 ( + IN PPCIPBUSDATA BusData, + IN PVOID State, + IN PUCHAR Buffer, + IN ULONG Offset + ); + + +// +// Globals +// + +KSPIN_LOCK HalpPCIConfigLock; + +PCI_CONFIG_HANDLER PCIConfigHandler; + +PCI_CONFIG_HANDLER PCIConfigHandlerType1 = { + HalpPCISynchronizeType1, + HalpPCIReleaseSynchronzationType1, + { + HalpPCIReadUlongType1, // 0 + HalpPCIReadUcharType1, // 1 + HalpPCIReadUshortType1 // 2 + }, + { + HalpPCIWriteUlongType1, // 0 + HalpPCIWriteUcharType1, // 1 + HalpPCIWriteUshortType1 // 2 + } +}; + +PCI_CONFIG_HANDLER PCIConfigHandlerType2 = { + HalpPCISynchronizeType2, + HalpPCIReleaseSynchronzationType2, + { + HalpPCIReadUlongType2, // 0 + HalpPCIReadUcharType2, // 1 + HalpPCIReadUshortType2 // 2 + }, + { + HalpPCIWriteUlongType2, // 0 + HalpPCIWriteUcharType2, // 1 + HalpPCIWriteUshortType2 // 2 + } +}; + +UCHAR PCIDeref[4][4] = { {0,1,2,2},{1,1,1,1},{2,1,2,2},{1,1,1,1} }; + +extern BOOLEAN HalpDoingCrashDump; + +// +// Orion B0 errata workaround +// + +struct { + PBUS_HANDLER Handler; + PCI_SLOT_NUMBER Slot; +} HalpOrionOPB; + +VOID +HalpPCIConfig ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PUCHAR Buffer, + IN ULONG Offset, + IN ULONG Length, + IN FncConfigIO *ConfigIO + ); + +#if DBG +#define DBGMSG(a) DbgPrint(a) +VOID +HalpTestPci ( + ULONG + ); +#else +#define DBGMSG(a) +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,HalpInitializePciBus) +#pragma alloc_text(INIT,HalpAllocateAndInitPciBusHandler) +#pragma alloc_text(INIT,HalpIsValidPCIDevice) +#pragma alloc_text(PAGE,HalpAssignPCISlotResources) +#pragma alloc_text(PAGELK,HalpPCISynchronizeOrionB0) +#pragma alloc_text(PAGELK,HalpPCIReleaseSynchronzationOrionB0) +#endif + + +VOID +HalpInitializePciBus ( + VOID + ) +{ + PPCI_REGISTRY_INFO PCIRegInfo; + UNICODE_STRING unicodeString, ConfigName, IdentName; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE hMFunc, hBus; + NTSTATUS status; + UCHAR buffer [sizeof(PPCI_REGISTRY_INFO) + 99]; + PWSTR p; + WCHAR wstr[8]; + ULONG i, d, junk, HwType, BusNo, f; + PBUS_HANDLER BusHandler; + PCI_SLOT_NUMBER SlotNumber; + PPCI_COMMON_CONFIG PciData; + UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; + PKEY_VALUE_FULL_INFORMATION ValueInfo; + PCM_FULL_RESOURCE_DESCRIPTOR Desc; + PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc; + ULONG OPBNumber; + BOOLEAN OPBA2B0Found, COPBInbPostingEnabled; + + // + // Search the hardware description looking for any reported + // PCI bus. The first ARC entry for a PCI bus will contain + // the PCI_REGISTRY_INFO. + + RtlInitUnicodeString (&unicodeString, rgzMultiFunctionAdapter); + InitializeObjectAttributes ( + &objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + NULL, // handle + NULL); + + + status = ZwOpenKey (&hMFunc, KEY_READ, &objectAttributes); + if (!NT_SUCCESS(status)) { + return ; + } + + unicodeString.Buffer = wstr; + unicodeString.MaximumLength = sizeof (wstr); + + RtlInitUnicodeString (&ConfigName, rgzConfigurationData); + RtlInitUnicodeString (&IdentName, rgzIdentifier); + + ValueInfo = (PKEY_VALUE_FULL_INFORMATION) buffer; + + for (i=0; TRUE; i++) { + RtlIntegerToUnicodeString (i, 10, &unicodeString); + InitializeObjectAttributes ( + &objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + hMFunc, + NULL); + + status = ZwOpenKey (&hBus, KEY_READ, &objectAttributes); + if (!NT_SUCCESS(status)) { + // + // Out of Multifunction adapter entries... + // + + ZwClose (hMFunc); + return ; + } + + // + // Check the Indentifier to see if this is a PCI entry + // + + status = ZwQueryValueKey ( + hBus, + &IdentName, + KeyValueFullInformation, + ValueInfo, + sizeof (buffer), + &junk + ); + + if (!NT_SUCCESS (status)) { + ZwClose (hBus); + continue; + } + + p = (PWSTR) ((PUCHAR) ValueInfo + ValueInfo->DataOffset); + if (p[0] != L'P' || p[1] != L'C' || p[2] != L'I' || p[3] != 0) { + ZwClose (hBus); + continue; + } + + // + // The first PCI entry has the PCI_REGISTRY_INFO structure + // attached to it. + // + + status = ZwQueryValueKey ( + hBus, + &ConfigName, + KeyValueFullInformation, + ValueInfo, + sizeof (buffer), + &junk + ); + + ZwClose (hBus); + if (!NT_SUCCESS(status)) { + continue ; + } + + Desc = (PCM_FULL_RESOURCE_DESCRIPTOR) ((PUCHAR) + ValueInfo + ValueInfo->DataOffset); + PDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR) + Desc->PartialResourceList.PartialDescriptors); + + if (PDesc->Type == CmResourceTypeDeviceSpecific) { + // got it.. + PCIRegInfo = (PPCI_REGISTRY_INFO) (PDesc+1); + break; + } + } + + // + // Initialize spinlock for synchronizing access to PCI space + // + + KeInitializeSpinLock (&HalpPCIConfigLock); + PciData = (PPCI_COMMON_CONFIG) iBuffer; + + // + // PCIRegInfo describes the system's PCI support as indicated by the BIOS. + // + + HwType = PCIRegInfo->HardwareMechanism & 0xf; + + // + // Some AMI bioses claim machines are Type2 configuration when they + // are really type1. If this is a Type2 with at least one bus, + // try to verify it's not really a type1 bus + // + + if (PCIRegInfo->NoBuses && HwType == 2) { + + // + // Check each slot for a valid device. Which every style configuration + // space shows a valid device first will be used + // + + SlotNumber.u.bits.Reserved = 0; + SlotNumber.u.bits.FunctionNumber = 0; + + for (d = 0; d < PCI_MAX_DEVICES; d++) { + SlotNumber.u.bits.DeviceNumber = d; + + // + // First try what the BIOS claims - type 2. Allocate type2 + // test handle for PCI bus 0. + // + + HwType = 2; + BusHandler = HalpAllocateAndInitPciBusHandler (HwType, 0, TRUE); + + if (HalpIsValidPCIDevice (BusHandler, SlotNumber)) { + break; + } + + // + // Valid device not found on Type2 access for this slot. + // Reallocate the bus handler are Type1 and take a look. + // + + HwType = 1; + BusHandler = HalpAllocateAndInitPciBusHandler (HwType, 0, TRUE); + + if (HalpIsValidPCIDevice (BusHandler, SlotNumber)) { + break; + } + + HwType = 2; + } + + // + // Reset handler for PCI bus 0 to whatever style config space + // was finally decided. + // + + HalpAllocateAndInitPciBusHandler (HwType, 0, FALSE); + } + + + // + // For each PCI bus present, allocate a handler structure and + // fill in the dispatch functions + // + + do { + for (i=0; i < PCIRegInfo->NoBuses; i++) { + + // + // If handler not already built, do it now + // + + if (!HalpHandlerForBus (PCIBus, i)) { + HalpAllocateAndInitPciBusHandler (HwType, i, FALSE); + } + } + + // + // Bus handlers for all PCI buses have been allocated, go collect + // pci bridge information. + // + + } while (HalpGetPciBridgeConfig (HwType, &PCIRegInfo->NoBuses)) ; + + // + // Fixup SUPPORTED_RANGES + // + + HalpFixupPciSupportedRanges (PCIRegInfo->NoBuses); + + + // + // Look for PCI controllers which have known work-arounds, and make + // sure they are applied. + // + + OPBNumber = 0; + OPBA2B0Found = FALSE; + COPBInbPostingEnabled = FALSE; + + SlotNumber.u.bits.Reserved = 0; + for (BusNo=0; BusNo < PCIRegInfo->NoBuses; BusNo++) { + BusHandler = HalpHandlerForBus (PCIBus, BusNo); + + for (d = 0; d < PCI_MAX_DEVICES; d++) { + SlotNumber.u.bits.DeviceNumber = d; + + for (f = 0; f < PCI_MAX_FUNCTION; f++) { + SlotNumber.u.bits.FunctionNumber = f; + + // + // Read PCI configuration information + // + + HalpReadPCIConfig (BusHandler, SlotNumber, PciData, 0, PCI_COMMON_HDR_LENGTH); + + // + // Check for chips with known work-arounds to apply + // + + if (PciData->VendorID == 0x8086 && + PciData->DeviceID == 0x04A3 && + PciData->RevisionID < 0x11) { + + // + // 82430 PCMC controller + // + + HalpReadPCIConfig (BusHandler, SlotNumber, buffer, 0x53, 2); + + buffer[0] &= ~0x08; // turn off bit 3 register 0x53 + + if (PciData->RevisionID == 0x10) { // on rev 0x10, also turn + buffer[1] &= ~0x01; // bit 0 register 0x54 + } + + HalpWritePCIConfig (BusHandler, SlotNumber, buffer, 0x53, 2); + } + + if (PciData->VendorID == 0x8086 && + PciData->DeviceID == 0x0484 && + PciData->RevisionID <= 3) { + + // + // 82378 ISA bridge & SIO + // + + HalpReadPCIConfig (BusHandler, SlotNumber, buffer, 0x41, 1); + + buffer[0] &= ~0x1; // turn off bit 0 register 0x41 + + HalpWritePCIConfig (BusHandler, SlotNumber, buffer, 0x41, 1); + } + + // + // Look for Orion PCI Bridge + // + + if (PciData->VendorID == 0x8086 && + PciData->DeviceID == 0x84c4 ) { + + // + // 82450 Orion PCI Bridge Workaround + // Need a workaround if following conditions are true: + // i) 2 OPBs present + // ii)There is an A2/B0 step OPB present. + // iii) Inbound posting on the compatibility OPB is + // enabled. + // NOTE: Inbound Posting on the non-compatibility OPB + // MUST BE disabled by BIOS + // + + OPBNumber += 1; + + if (PciData->RevisionID <= 4) { + OPBA2B0Found = TRUE; + } + + if (SlotNumber.u.bits.DeviceNumber == (0xc8>>3)) { + + // Found compatibility OPB. Determine if the compatibility + // OPB has inbound posting enabled by testing bit 0 of reg 54 + + HalpReadPCIConfig (BusHandler, SlotNumber, buffer, 0x54, 2); + COPBInbPostingEnabled = (buffer[0] & 0x1) ? TRUE : FALSE; + + } else { + + // The compatibility OPB ALWAYS has a device + // number 0xc8. Save the ncOPB slot number + // and BusHandler + + HalpOrionOPB.Slot = SlotNumber; + HalpOrionOPB.Handler = BusHandler; + } + } + } // next function + } // next device + } // next bus + + // + // Is Orion B0 workaround needed? + // + + if (OPBNumber >= 2 && OPBA2B0Found && COPBInbPostingEnabled) { + + // + // Replace synchronization functions with Orion specific functions + // + + ASSERT (PCIConfigHandler.Synchronize == HalpPCISynchronizeType1); + MmLockPagableCodeSection (&HalpPCISynchronizeOrionB0); + PCIConfigHandler.Synchronize = HalpPCISynchronizeOrionB0; + PCIConfigHandler.ReleaseSynchronzation = HalpPCIReleaseSynchronzationOrionB0; + } + +#if DBG + HalpTestPci (0); +#endif +} + + +PBUS_HANDLER +HalpAllocateAndInitPciBusHandler ( + IN ULONG HwType, + IN ULONG BusNo, + IN BOOLEAN TestAllocation + ) +{ + PBUS_HANDLER Bus; + PPCIPBUSDATA BusData; + + Bus = HalpAllocateBusHandler ( + PCIBus, // Interface type + PCIConfiguration, // Has this configuration space + BusNo, // bus # + Internal, // child of this bus + 0, // and number + sizeof (PCIPBUSDATA) // sizeof bus specific buffer + ); + + // + // Fill in PCI handlers + // + + Bus->GetBusData = (PGETSETBUSDATA) HalpGetPCIData; + Bus->SetBusData = (PGETSETBUSDATA) HalpSetPCIData; + Bus->GetInterruptVector = (PGETINTERRUPTVECTOR) HalpGetPCIIntOnISABus; + Bus->AdjustResourceList = (PADJUSTRESOURCELIST) HalpAdjustPCIResourceList; + Bus->AssignSlotResources = (PASSIGNSLOTRESOURCES) HalpAssignPCISlotResources; + Bus->BusAddresses->Dma.Limit = 0; + + BusData = (PPCIPBUSDATA) Bus->BusData; + + // + // Fill in common PCI data + // + + BusData->CommonData.Tag = PCI_DATA_TAG; + BusData->CommonData.Version = PCI_DATA_VERSION; + BusData->CommonData.ReadConfig = (PciReadWriteConfig) HalpReadPCIConfig; + BusData->CommonData.WriteConfig = (PciReadWriteConfig) HalpWritePCIConfig; + BusData->CommonData.Pin2Line = (PciPin2Line) HalpPCIPin2ISALine; + BusData->CommonData.Line2Pin = (PciLine2Pin) HalpPCIISALine2Pin; + + // + // Set defaults + // + + BusData->MaxDevice = PCI_MAX_DEVICES; + BusData->GetIrqRange = (PciIrqRange) HalpGetISAFixedPCIIrq; + + RtlInitializeBitMap (&BusData->DeviceConfigured, + BusData->ConfiguredBits, 256); + + switch (HwType) { + case 1: + // + // Initialize access port information for Type1 handlers + // + + RtlCopyMemory (&PCIConfigHandler, + &PCIConfigHandlerType1, + sizeof (PCIConfigHandler)); + + BusData->Config.Type1.Address = PCI_TYPE1_ADDR_PORT; + BusData->Config.Type1.Data = PCI_TYPE1_DATA_PORT; + break; + + case 2: + // + // Initialize access port information for Type2 handlers + // + + RtlCopyMemory (&PCIConfigHandler, + &PCIConfigHandlerType2, + sizeof (PCIConfigHandler)); + + BusData->Config.Type2.CSE = PCI_TYPE2_CSE_PORT; + BusData->Config.Type2.Forward = PCI_TYPE2_FORWARD_PORT; + BusData->Config.Type2.Base = PCI_TYPE2_ADDRESS_BASE; + + // + // Early PCI machines didn't decode the last bit of + // the device id. Shrink type 2 support max device. + // + BusData->MaxDevice = 0x10; + + break; + + default: + // unsupport type + DBGMSG ("HAL: Unkown PCI type\n"); + } + + if (!TestAllocation) { +#ifdef SUBCLASSPCI + HalpSubclassPCISupport (Bus, HwType); +#endif + } + + return Bus; +} + +BOOLEAN +HalpIsValidPCIDevice ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot + ) +/*++ + +Routine Description: + + Reads the device configuration data for the given slot and + returns TRUE if the configuration data appears to be valid for + a PCI device; otherwise returns FALSE. + +Arguments: + + BusHandler - Bus to check + Slot - Slot to check + +--*/ + +{ + PPCI_COMMON_CONFIG PciData; + UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; + ULONG i, j; + + + PciData = (PPCI_COMMON_CONFIG) iBuffer; + + // + // Read device common header + // + + HalpReadPCIConfig (BusHandler, Slot, PciData, 0, PCI_COMMON_HDR_LENGTH); + + // + // Valid device header? + // + + if (PciData->VendorID == PCI_INVALID_VENDORID || + PCI_CONFIG_TYPE (PciData) != PCI_DEVICE_TYPE) { + + return FALSE; + } + + // + // Check fields for reasonable values + // + + if ((PciData->u.type0.InterruptPin && PciData->u.type0.InterruptPin > 4) || + (PciData->u.type0.InterruptLine & 0x70)) { + return FALSE; + } + + for (i=0; i < PCI_TYPE0_ADDRESSES; i++) { + j = PciData->u.type0.BaseAddresses[i]; + + if (j & PCI_ADDRESS_IO_SPACE) { + if (j > 0xffff) { + // IO port > 64k? + return FALSE; + } + } else { + if (j > 0xf && j < 0x80000) { + // Mem address < 0x8000h? + return FALSE; + } + } + + if (Is64BitBaseAddress(j)) { + i += 1; + } + } + + // + // Guess it's a valid device.. + // + + return TRUE; +} + + + + + +ULONG +HalpGetPCIData ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER Slot, + IN PUCHAR Buffer, + IN ULONG Offset, + IN ULONG Length + ) +/*++ + +Routine Description: + + The function returns the Pci bus data for a device. + +Arguments: + + BusNumber - Indicates which bus. + + VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word) + + Buffer - Supplies the space to store the data. + + Length - Supplies a count in bytes of the maximum amount to return. + +Return Value: + + Returns the amount of data stored into the buffer. + + If this PCI slot has never been set, then the configuration information + returned is zeroed. + + +--*/ +{ + PPCI_COMMON_CONFIG PciData; + UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; + PPCIPBUSDATA BusData; + ULONG Len; + ULONG i, bit; + + if (Length > sizeof (PCI_COMMON_CONFIG)) { + Length = sizeof (PCI_COMMON_CONFIG); + } + + Len = 0; + PciData = (PPCI_COMMON_CONFIG) iBuffer; + + if (Offset >= PCI_COMMON_HDR_LENGTH) { + // + // The user did not request any data from the common + // header. Verify the PCI device exists, then continue + // in the device specific area. + // + + HalpReadPCIConfig (BusHandler, Slot, PciData, 0, sizeof(ULONG)); + + if (PciData->VendorID == PCI_INVALID_VENDORID) { + return 0; + } + + } else { + + // + // Caller requested at least some data within the + // common header. Read the whole header, effect the + // fields we need to and then copy the user's requested + // bytes from the header + // + + BusData = (PPCIPBUSDATA) BusHandler->BusData; + + // + // Read this PCI devices slot data + // + + Len = PCI_COMMON_HDR_LENGTH; + HalpReadPCIConfig (BusHandler, Slot, PciData, 0, Len); + + if (PciData->VendorID == PCI_INVALID_VENDORID) { + PciData->VendorID = PCI_INVALID_VENDORID; + Len = 2; // only return invalid id + + } else { + + BusData->CommonData.Pin2Line (BusHandler, RootHandler, Slot, PciData); + } + + // + // Has this PCI device been configured? + // + +#if 0 + + // + // On DBG build, if this PCI device has not yet been configured, + // then don't report any current configuration the device may have. + // + + bit = PciBitIndex(Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber); + + if (!RtlCheckBit(&BusData->DeviceConfigured, bit) && + PCI_CONFIG_TYPE (PciData) == PCI_DEVICE_TYPE) { + + for (i=0; i < PCI_TYPE0_ADDRESSES; i++) { + PciData->u.type0.BaseAddresses[i] = 0; + } + + PciData->u.type0.ROMBaseAddress = 0; + PciData->Command &= ~(PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE); + } +#endif + + + // + // Copy whatever data overlaps into the callers buffer + // + + if (Len < Offset) { + // no data at caller's buffer + return 0; + } + + Len -= Offset; + if (Len > Length) { + Len = Length; + } + + RtlMoveMemory(Buffer, iBuffer + Offset, Len); + + Offset += Len; + Buffer += Len; + Length -= Len; + } + + if (Length) { + if (Offset >= PCI_COMMON_HDR_LENGTH) { + // + // The remaining Buffer comes from the Device Specific + // area - put on the kitten gloves and read from it. + // + // Specific read/writes to the PCI device specific area + // are guarenteed: + // + // Not to read/write any byte outside the area specified + // by the caller. (this may cause WORD or BYTE references + // to the area in order to read the non-dword aligned + // ends of the request) + // + // To use a WORD access if the requested length is exactly + // a WORD long. + // + // To use a BYTE access if the requested length is exactly + // a BYTE long. + // + + HalpReadPCIConfig (BusHandler, Slot, Buffer, Offset, Length); + Len += Length; + } + } + + return Len; +} + +ULONG +HalpSetPCIData ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER Slot, + IN PUCHAR Buffer, + IN ULONG Offset, + IN ULONG Length + ) +/*++ + +Routine Description: + + The function returns the Pci bus data for a device. + +Arguments: + + + VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word) + + Buffer - Supplies the space to store the data. + + Length - Supplies a count in bytes of the maximum amount to return. + +Return Value: + + Returns the amount of data stored into the buffer. + +--*/ +{ + PPCI_COMMON_CONFIG PciData, PciData2; + UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; + UCHAR iBuffer2[PCI_COMMON_HDR_LENGTH]; + PPCIPBUSDATA BusData; + ULONG Len, cnt; + + + if (Length > sizeof (PCI_COMMON_CONFIG)) { + Length = sizeof (PCI_COMMON_CONFIG); + } + + + Len = 0; + PciData = (PPCI_COMMON_CONFIG) iBuffer; + PciData2 = (PPCI_COMMON_CONFIG) iBuffer2; + + + if (Offset >= PCI_COMMON_HDR_LENGTH) { + // + // The user did not request any data from the common + // header. Verify the PCI device exists, then continue in + // the device specific area. + // + + HalpReadPCIConfig (BusHandler, Slot, PciData, 0, sizeof(ULONG)); + + if (PciData->VendorID == PCI_INVALID_VENDORID) { + return 0; + } + + } else { + + // + // Caller requested to set at least some data within the + // common header. + // + + Len = PCI_COMMON_HDR_LENGTH; + HalpReadPCIConfig (BusHandler, Slot, PciData, 0, Len); + if (PciData->VendorID == PCI_INVALID_VENDORID || + PCI_CONFIG_TYPE (PciData) != PCI_DEVICE_TYPE) { + + // no device, or header type unkown + return 0; + } + + + // + // Set this device as configured + // + + BusData = (PPCIPBUSDATA) BusHandler->BusData; +#if DBG + cnt = PciBitIndex(Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber); + RtlSetBits (&BusData->DeviceConfigured, cnt, 1); +#endif + // + // Copy COMMON_HDR values to buffer2, then overlay callers changes. + // + + RtlMoveMemory (iBuffer2, iBuffer, Len); + BusData->CommonData.Pin2Line (BusHandler, RootHandler, Slot, PciData2); + + Len -= Offset; + if (Len > Length) { + Len = Length; + } + + RtlMoveMemory (iBuffer2+Offset, Buffer, Len); + + // in case interrupt line or pin was editted + BusData->CommonData.Line2Pin (BusHandler, RootHandler, Slot, PciData2, PciData); + +#if DBG + // + // Verify R/O fields haven't changed + // + if (PciData2->VendorID != PciData->VendorID || + PciData2->DeviceID != PciData->DeviceID || + PciData2->RevisionID != PciData->RevisionID || + PciData2->ProgIf != PciData->ProgIf || + PciData2->SubClass != PciData->SubClass || + PciData2->BaseClass != PciData->BaseClass || + PciData2->HeaderType != PciData->HeaderType || + PciData2->BaseClass != PciData->BaseClass || + PciData2->u.type0.MinimumGrant != PciData->u.type0.MinimumGrant || + PciData2->u.type0.MaximumLatency != PciData->u.type0.MaximumLatency) { + DbgPrint ("PCI SetBusData: Read-Only configuration value changed\n"); + } +#endif + // + // Set new PCI configuration + // + + HalpWritePCIConfig (BusHandler, Slot, iBuffer2+Offset, Offset, Len); + + Offset += Len; + Buffer += Len; + Length -= Len; + } + + if (Length) { + if (Offset >= PCI_COMMON_HDR_LENGTH) { + // + // The remaining Buffer comes from the Device Specific + // area - put on the kitten gloves and write it + // + // Specific read/writes to the PCI device specific area + // are guarenteed: + // + // Not to read/write any byte outside the area specified + // by the caller. (this may cause WORD or BYTE references + // to the area in order to read the non-dword aligned + // ends of the request) + // + // To use a WORD access if the requested length is exactly + // a WORD long. + // + // To use a BYTE access if the requested length is exactly + // a BYTE long. + // + + HalpWritePCIConfig (BusHandler, Slot, Buffer, Offset, Length); + Len += Length; + } + } + + return Len; +} + +VOID +HalpReadPCIConfig ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ) +{ + if (!HalpValidPCISlot (BusHandler, Slot)) { + // + // Invalid SlotID return no data + // + + RtlFillMemory (Buffer, Length, (UCHAR) -1); + return ; + } + + HalpPCIConfig (BusHandler, Slot, (PUCHAR) Buffer, Offset, Length, + PCIConfigHandler.ConfigRead); +} + +VOID +HalpWritePCIConfig ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ) +{ + if (!HalpValidPCISlot (BusHandler, Slot)) { + // + // Invalid SlotID do nothing + // + return ; + } + + HalpPCIConfig (BusHandler, Slot, (PUCHAR) Buffer, Offset, Length, + PCIConfigHandler.ConfigWrite); +} + +BOOLEAN +HalpValidPCISlot ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot + ) +{ + PCI_SLOT_NUMBER Slot2; + PPCIPBUSDATA BusData; + UCHAR HeaderType; + ULONG i; + + BusData = (PPCIPBUSDATA) BusHandler->BusData; + + if (Slot.u.bits.Reserved != 0) { + return FALSE; + } + + if (Slot.u.bits.DeviceNumber >= BusData->MaxDevice) { + return FALSE; + } + + if (Slot.u.bits.FunctionNumber == 0) { + return TRUE; + } + + // + // Non zero function numbers are only supported if the + // device has the PCI_MULTIFUNCTION bit set in it's header + // + + i = Slot.u.bits.DeviceNumber; + + // + // Read DeviceNumber, Function zero, to determine if the + // PCI supports multifunction devices + // + + Slot2 = Slot; + Slot2.u.bits.FunctionNumber = 0; + + HalpReadPCIConfig ( + BusHandler, + Slot2, + &HeaderType, + FIELD_OFFSET (PCI_COMMON_CONFIG, HeaderType), + sizeof (UCHAR) + ); + + if (!(HeaderType & PCI_MULTIFUNCTION) || HeaderType == 0xFF) { + // this device doesn't exists or doesn't support MULTIFUNCTION types + return FALSE; + } + + return TRUE; +} + + +VOID +HalpPCIConfig ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PUCHAR Buffer, + IN ULONG Offset, + IN ULONG Length, + IN FncConfigIO *ConfigIO + ) +{ + KIRQL OldIrql; + ULONG i; + UCHAR State[20]; + PPCIPBUSDATA BusData; + + BusData = (PPCIPBUSDATA) BusHandler->BusData; + PCIConfigHandler.Synchronize (BusHandler, Slot, &OldIrql, State); + + while (Length) { + i = PCIDeref[Offset % sizeof(ULONG)][Length % sizeof(ULONG)]; + i = ConfigIO[i] (BusData, State, Buffer, Offset); + + Offset += i; + Buffer += i; + Length -= i; + } + + PCIConfigHandler.ReleaseSynchronzation (BusHandler, OldIrql); +} + +VOID +HalpPCISynchronizeType1 ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PKIRQL Irql, + IN PPCI_TYPE1_CFG_BITS PciCfg1 + ) +{ + // + // Initialize PciCfg1 + // + + PciCfg1->u.AsULONG = 0; + PciCfg1->u.bits.BusNumber = BusHandler->BusNumber; + PciCfg1->u.bits.DeviceNumber = Slot.u.bits.DeviceNumber; + PciCfg1->u.bits.FunctionNumber = Slot.u.bits.FunctionNumber; + PciCfg1->u.bits.Enable = TRUE; + + // + // Synchronize with PCI type1 config space + // + + if (!HalpDoingCrashDump) { + *Irql = KfRaiseIrql (HIGH_LEVEL); + KiAcquireSpinLock (&HalpPCIConfigLock); + } else { + *Irql = HIGH_LEVEL; + } +} + +VOID +HalpPCIReleaseSynchronzationType1 ( + IN PBUS_HANDLER BusHandler, + IN KIRQL Irql + ) +{ + PCI_TYPE1_CFG_BITS PciCfg1; + PPCIPBUSDATA BusData; + + // + // Disable PCI configuration space + // + + PciCfg1.u.AsULONG = 0; + BusData = (PPCIPBUSDATA) BusHandler->BusData; + WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1.u.AsULONG); + + // + // Release spinlock + // + + if (!HalpDoingCrashDump) { + KiReleaseSpinLock (&HalpPCIConfigLock); + KfLowerIrql (Irql); + } +} + + +VOID +HalpPCISynchronizeOrionB0 ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PKIRQL Irql, + IN PPCI_TYPE1_CFG_BITS PciCfg1 + ) +{ + PCI_TYPE1_CFG_BITS Cfg1; + union { + ULONG dword; + USHORT word; + UCHAR byte[4]; + } Buffer; + + // + // First perform normal type 1 synchronization + // + + HalpPCISynchronizeType1 (BusHandler, Slot, Irql, PciCfg1); + + // + // Apply Orion B0 workaround + // + + Cfg1.u.AsULONG=0; + Cfg1.u.bits.BusNumber = HalpOrionOPB.Handler->BusNumber; + Cfg1.u.bits.DeviceNumber = HalpOrionOPB.Slot.u.bits.DeviceNumber; + Cfg1.u.bits.FunctionNumber = HalpOrionOPB.Slot.u.bits.FunctionNumber; + Cfg1.u.bits.Enable = TRUE; + + // + // Read OPB until we get back the expected Vendor ID and device ID + // + + do { + HalpPCIReadUlongType1 (HalpOrionOPB.Handler->BusData, &Cfg1, Buffer.byte, 0); + } while (Buffer.dword != 0x84c48086); + + // + // The bug is that the config read will return whatever value you + // happened to read last. Read register 0x54 till we don't read the + // last value read any more(Vendor ID/Device ID). + // + + do { + HalpPCIReadUshortType1 (HalpOrionOPB.Handler->BusData, &Cfg1, Buffer.byte, 0x54); + } while (Buffer.word == 0x8086); + + // + // Disable inbound posting by clearing bit 0 of register 0x54 + // + + Buffer.word &= ~0x1; + HalpPCIWriteUshortType1 (HalpOrionOPB.Handler->BusData, &Cfg1, Buffer.byte, 0x54); +} + +VOID +HalpPCIReleaseSynchronzationOrionB0 ( + IN PBUS_HANDLER BusHandler, + IN KIRQL Irql + ) +{ + + PCI_TYPE1_CFG_BITS PciCfg1; + PPCIPBUSDATA BusData; + union { + ULONG dword; + USHORT word; + UCHAR byte[4]; + } Buffer; + + PciCfg1.u.AsULONG=0; + PciCfg1.u.bits.BusNumber = HalpOrionOPB.Handler->BusNumber; + PciCfg1.u.bits.DeviceNumber = HalpOrionOPB.Slot.u.bits.DeviceNumber; + PciCfg1.u.bits.FunctionNumber = HalpOrionOPB.Slot.u.bits.FunctionNumber; + PciCfg1.u.bits.Enable = TRUE; + + HalpPCIReadUshortType1 (HalpOrionOPB.Handler->BusData, &PciCfg1, Buffer.byte, 0x54); + + + // + // Enable Inbound posting by setting bit 0 of register 0x54 of ncOPB + // + + Buffer.word |= 0x1; + HalpPCIWriteUshortType1 (HalpOrionOPB.Handler->BusData, &PciCfg1, Buffer.byte, 0x54); + + // + // Complete type 1 synchronization + // + + HalpPCIReleaseSynchronzationType1 (BusHandler, Irql); +} + + + +ULONG +HalpPCIReadUcharType1 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE1_CFG_BITS PciCfg1, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + ULONG i; + + i = Offset % sizeof(ULONG); + PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); + WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); + *Buffer = READ_PORT_UCHAR ((PUCHAR) (BusData->Config.Type1.Data + i)); + return sizeof (UCHAR); +} + +ULONG +HalpPCIReadUshortType1 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE1_CFG_BITS PciCfg1, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + ULONG i; + + i = Offset % sizeof(ULONG); + PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); + WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); + *((PUSHORT) Buffer) = READ_PORT_USHORT ((PUSHORT) (BusData->Config.Type1.Data + i)); + return sizeof (USHORT); +} + +ULONG +HalpPCIReadUlongType1 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE1_CFG_BITS PciCfg1, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); + WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); + *((PULONG) Buffer) = READ_PORT_ULONG ((PULONG) BusData->Config.Type1.Data); + return sizeof (ULONG); +} + + +ULONG +HalpPCIWriteUcharType1 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE1_CFG_BITS PciCfg1, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + ULONG i; + + i = Offset % sizeof(ULONG); + PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); + WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); + WRITE_PORT_UCHAR ((PUCHAR) (BusData->Config.Type1.Data + i), *Buffer); + return sizeof (UCHAR); +} + +ULONG +HalpPCIWriteUshortType1 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE1_CFG_BITS PciCfg1, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + ULONG i; + + i = Offset % sizeof(ULONG); + PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); + WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); + WRITE_PORT_USHORT ((PUSHORT) (BusData->Config.Type1.Data + i), *((PUSHORT) Buffer)); + return sizeof (USHORT); +} + +ULONG +HalpPCIWriteUlongType1 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE1_CFG_BITS PciCfg1, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); + WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); + WRITE_PORT_ULONG ((PULONG) BusData->Config.Type1.Data, *((PULONG) Buffer)); + return sizeof (ULONG); +} + + +VOID HalpPCISynchronizeType2 ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PKIRQL Irql, + IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr + ) +{ + PCI_TYPE2_CSE_BITS PciCfg2Cse; + PPCIPBUSDATA BusData; + + BusData = (PPCIPBUSDATA) BusHandler->BusData; + + // + // Initialize Cfg2Addr + // + + PciCfg2Addr->u.AsUSHORT = 0; + PciCfg2Addr->u.bits.Agent = (USHORT) Slot.u.bits.DeviceNumber; + PciCfg2Addr->u.bits.AddressBase = (USHORT) BusData->Config.Type2.Base; + + // + // Synchronize with type2 config space - type2 config space + // remaps 4K of IO space, so we can not allow other I/Os to occur + // while using type2 config space. + // + + HalpPCIAcquireType2Lock (&HalpPCIConfigLock, Irql); + + PciCfg2Cse.u.AsUCHAR = 0; + PciCfg2Cse.u.bits.Enable = TRUE; + PciCfg2Cse.u.bits.FunctionNumber = (UCHAR) Slot.u.bits.FunctionNumber; + PciCfg2Cse.u.bits.Key = 0xff; + + // + // Select bus & enable type 2 configuration space + // + + WRITE_PORT_UCHAR (BusData->Config.Type2.Forward, (UCHAR) BusHandler->BusNumber); + WRITE_PORT_UCHAR (BusData->Config.Type2.CSE, PciCfg2Cse.u.AsUCHAR); +} + + +VOID HalpPCIReleaseSynchronzationType2 ( + IN PBUS_HANDLER BusHandler, + IN KIRQL Irql + ) +{ + PCI_TYPE2_CSE_BITS PciCfg2Cse; + PPCIPBUSDATA BusData; + + // + // disable PCI configuration space + // + + BusData = (PPCIPBUSDATA) BusHandler->BusData; + + PciCfg2Cse.u.AsUCHAR = 0; + WRITE_PORT_UCHAR (BusData->Config.Type2.CSE, PciCfg2Cse.u.AsUCHAR); + WRITE_PORT_UCHAR (BusData->Config.Type2.Forward, (UCHAR) 0); + + // + // Restore interrupts, release spinlock + // + + HalpPCIReleaseType2Lock (&HalpPCIConfigLock, Irql); +} + + +ULONG +HalpPCIReadUcharType2 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; + *Buffer = READ_PORT_UCHAR ((PUCHAR) PciCfg2Addr->u.AsUSHORT); + return sizeof (UCHAR); +} + +ULONG +HalpPCIReadUshortType2 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; + *((PUSHORT) Buffer) = READ_PORT_USHORT ((PUSHORT) PciCfg2Addr->u.AsUSHORT); + return sizeof (USHORT); +} + +ULONG +HalpPCIReadUlongType2 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; + *((PULONG) Buffer) = READ_PORT_ULONG ((PULONG) PciCfg2Addr->u.AsUSHORT); + return sizeof(ULONG); +} + + +ULONG +HalpPCIWriteUcharType2 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; + WRITE_PORT_UCHAR ((PUCHAR) PciCfg2Addr->u.AsUSHORT, *Buffer); + return sizeof (UCHAR); +} + +ULONG +HalpPCIWriteUshortType2 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; + WRITE_PORT_USHORT ((PUSHORT) PciCfg2Addr->u.AsUSHORT, *((PUSHORT) Buffer)); + return sizeof (USHORT); +} + +ULONG +HalpPCIWriteUlongType2 ( + IN PPCIPBUSDATA BusData, + IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, + IN PUCHAR Buffer, + IN ULONG Offset + ) +{ + PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; + WRITE_PORT_ULONG ((PULONG) PciCfg2Addr->u.AsUSHORT, *((PULONG) Buffer)); + return sizeof(ULONG); +} + + +NTSTATUS +HalpAssignPCISlotResources ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PUNICODE_STRING RegistryPath, + IN PUNICODE_STRING DriverClassName OPTIONAL, + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT DeviceObject OPTIONAL, + IN ULONG Slot, + IN OUT PCM_RESOURCE_LIST *pAllocatedResources + ) +/*++ + +Routine Description: + + Reads the targeted device to determine it's required resources. + Calls IoAssignResources to allocate them. + Sets the targeted device with it's assigned resoruces + and returns the assignments to the caller. + +Arguments: + +Return Value: + + STATUS_SUCCESS or error + +--*/ +{ + NTSTATUS status; + PUCHAR WorkingPool; + PPCI_COMMON_CONFIG PciData, PciOrigData, PciData2; + PCI_SLOT_NUMBER PciSlot; + PPCIPBUSDATA BusData; + PIO_RESOURCE_REQUIREMENTS_LIST CompleteList; + PIO_RESOURCE_DESCRIPTOR Descriptor; + PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor; + ULONG BusNumber; + ULONG i, j, m, length, memtype; + ULONG NoBaseAddress, RomIndex, Option; + PULONG BaseAddress[PCI_TYPE0_ADDRESSES + 1]; + PULONG OrigAddress[PCI_TYPE0_ADDRESSES + 1]; + BOOLEAN Match, EnableRomBase, RequestedInterrupt; + + + *pAllocatedResources = NULL; + PciSlot = *((PPCI_SLOT_NUMBER) &Slot); + BusNumber = BusHandler->BusNumber; + BusData = (PPCIPBUSDATA) BusHandler->BusData; + + // + // Allocate some pool for working space + // + + i = sizeof (IO_RESOURCE_REQUIREMENTS_LIST) + + sizeof (IO_RESOURCE_DESCRIPTOR) * (PCI_TYPE0_ADDRESSES + 2) * 2 + + PCI_COMMON_HDR_LENGTH * 3; + + WorkingPool = (PUCHAR) ExAllocatePool (PagedPool, i); + if (!WorkingPool) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Zero initialize pool, and get pointers into memory + // + + RtlZeroMemory (WorkingPool, i); + CompleteList = (PIO_RESOURCE_REQUIREMENTS_LIST) WorkingPool; + PciData = (PPCI_COMMON_CONFIG) (WorkingPool + i - PCI_COMMON_HDR_LENGTH * 3); + PciData2 = (PPCI_COMMON_CONFIG) (WorkingPool + i - PCI_COMMON_HDR_LENGTH * 2); + PciOrigData = (PPCI_COMMON_CONFIG) (WorkingPool + i - PCI_COMMON_HDR_LENGTH * 1); + + // + // Read the PCI device's configuration + // + + HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH); + if (PciData->VendorID == PCI_INVALID_VENDORID) { + ExFreePool (WorkingPool); + return STATUS_NO_SUCH_DEVICE; + } + + // + // For now since there's not PnP support in the OS, if the BIOS hasn't + // enable a VGA device don't allow it to get enabled via this interface. + // + + if ( (PciData->BaseClass == 0 && PciData->SubClass == 1) || + (PciData->BaseClass == 3 && PciData->SubClass == 0)) { + + if ((PciData->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE)) == 0) { + ExFreePool (WorkingPool); + return STATUS_DEVICE_NOT_CONNECTED; + } + } + + // + // Make a copy of the device's current settings + // + + RtlMoveMemory (PciOrigData, PciData, PCI_COMMON_HDR_LENGTH); + + // + // Initialize base addresses base on configuration data type + // + + switch (PCI_CONFIG_TYPE(PciData)) { + case 0 : + NoBaseAddress = PCI_TYPE0_ADDRESSES+1; + for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { + BaseAddress[j] = &PciData->u.type0.BaseAddresses[j]; + OrigAddress[j] = &PciOrigData->u.type0.BaseAddresses[j]; + } + BaseAddress[j] = &PciData->u.type0.ROMBaseAddress; + OrigAddress[j] = &PciOrigData->u.type0.ROMBaseAddress; + RomIndex = j; + break; + case 1: + NoBaseAddress = PCI_TYPE1_ADDRESSES+1; + for (j=0; j < PCI_TYPE1_ADDRESSES; j++) { + BaseAddress[j] = &PciData->u.type1.BaseAddresses[j]; + OrigAddress[j] = &PciOrigData->u.type1.BaseAddresses[j]; + } + BaseAddress[j] = &PciData->u.type1.ROMBaseAddress; + OrigAddress[j] = &PciOrigData->u.type1.ROMBaseAddress; + RomIndex = j; + break; + + default: + ExFreePool (WorkingPool); + return STATUS_NO_SUCH_DEVICE; + } + + // + // If the BIOS doesn't have the device's ROM enabled, then we won't + // enable it either. Remove it from the list. + // + + EnableRomBase = TRUE; + if (!(*BaseAddress[RomIndex] & PCI_ROMADDRESS_ENABLED)) { + ASSERT (RomIndex+1 == NoBaseAddress); + EnableRomBase = FALSE; + NoBaseAddress -= 1; + } + + // + // Set resources to all bits on to see what type of resources + // are required. + // + + for (j=0; j < NoBaseAddress; j++) { + *BaseAddress[j] = 0xFFFFFFFF; + } + + PciData->Command &= ~(PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE); + *BaseAddress[RomIndex] &= ~PCI_ROMADDRESS_ENABLED; + HalpWritePCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH); + HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH); + + // note type0 & type1 overlay ROMBaseAddress, InterruptPin, and InterruptLine + BusData->CommonData.Pin2Line (BusHandler, RootHandler, PciSlot, PciData); + + // + // Build an IO_RESOURCE_REQUIREMENTS_LIST for the PCI device + // + + CompleteList->InterfaceType = PCIBus; + CompleteList->BusNumber = BusNumber; + CompleteList->SlotNumber = Slot; + CompleteList->AlternativeLists = 1; + + CompleteList->List[0].Version = 1; + CompleteList->List[0].Revision = 1; + + Descriptor = CompleteList->List[0].Descriptors; + + // + // If PCI device has an interrupt resource, add it + // + + RequestedInterrupt = FALSE; + if (PciData->u.type0.InterruptPin && + PciData->u.type0.InterruptLine != (0 ^ IRQXOR) && + PciData->u.type0.InterruptLine != (0xFF ^ IRQXOR)) { + + RequestedInterrupt = TRUE; + CompleteList->List[0].Count++; + + Descriptor->Option = 0; + Descriptor->Type = CmResourceTypeInterrupt; + Descriptor->ShareDisposition = CmResourceShareShared; + Descriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; + + // Fill in any vector here - we'll pick it back up in + // HalAdjustResourceList and adjust it to it's allowed settings + Descriptor->u.Interrupt.MinimumVector = 0; + Descriptor->u.Interrupt.MaximumVector = 0xff; + Descriptor++; + } + + // + // Add a memory/port resoruce for each PCI resource + // + + // Clear ROM reserved bits + + *BaseAddress[RomIndex] &= ~0x7FF; + + for (j=0; j < NoBaseAddress; j++) { + if (*BaseAddress[j]) { + i = *BaseAddress[j]; + + // scan for first set bit, that's the length & alignment + length = 1 << (i & PCI_ADDRESS_IO_SPACE ? 2 : 4); + while (!(i & length) && length) { + length <<= 1; + } + + // scan for last set bit, that's the maxaddress + 1 + for (m = length; i & m; m <<= 1) ; + m--; + + // check for hosed PCI configuration requirements + if (length & ~m) { +#if DBG + DbgPrint ("PCI: defective device! Bus %d, Slot %d, Function %d\n", + BusNumber, + PciSlot.u.bits.DeviceNumber, + PciSlot.u.bits.FunctionNumber + ); + + DbgPrint ("PCI: BaseAddress[%d] = %08lx\n", j, i); +#endif + // the device is in error - punt. don't allow this + // resource any option - it either gets set to whatever + // bits it was able to return, or it doesn't get set. + + if (i & PCI_ADDRESS_IO_SPACE) { + m = i & ~0x3; + Descriptor->u.Port.MinimumAddress.LowPart = m; + } else { + m = i & ~0xf; + Descriptor->u.Memory.MinimumAddress.LowPart = m; + } + + m += length; // max address is min address + length + } + + // + // Add requested resource + // + + Descriptor->Option = 0; + if (i & PCI_ADDRESS_IO_SPACE) { + memtype = 0; + + if (!Is64BitBaseAddress(i) && + PciOrigData->Command & PCI_ENABLE_IO_SPACE) { + + // + // The IO range is/was already enabled at some location, add that + // as it's preferred setting. + // + + Descriptor->Type = CmResourceTypePort; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Flags = CM_RESOURCE_PORT_IO; + Descriptor->Option = IO_RESOURCE_PREFERRED; + + Descriptor->u.Port.Length = length; + Descriptor->u.Port.Alignment = length; + Descriptor->u.Port.MinimumAddress.LowPart = *OrigAddress[j] & ~0x3; + Descriptor->u.Port.MaximumAddress.LowPart = + Descriptor->u.Port.MinimumAddress.LowPart + length - 1; + + CompleteList->List[0].Count++; + Descriptor++; + + Descriptor->Option = IO_RESOURCE_ALTERNATIVE; + } + + // + // Add this IO range + // + + Descriptor->Type = CmResourceTypePort; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Flags = CM_RESOURCE_PORT_IO; + + Descriptor->u.Port.Length = length; + Descriptor->u.Port.Alignment = length; + Descriptor->u.Port.MaximumAddress.LowPart = m; + + } else { + + memtype = i & PCI_ADDRESS_MEMORY_TYPE_MASK; + + Descriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE; + if (j == RomIndex) { + // this is a ROM address + Descriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY; + } + + if (i & PCI_ADDRESS_MEMORY_PREFETCHABLE) { + Descriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE; + } + + if (!Is64BitBaseAddress(i) && + (j == RomIndex || + PciOrigData->Command & PCI_ENABLE_MEMORY_SPACE)) { + + // + // The memory range is/was already enabled at some location, add that + // as it's preferred setting. + // + + Descriptor->Type = CmResourceTypeMemory; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Option = IO_RESOURCE_PREFERRED; + + Descriptor->u.Port.Length = length; + Descriptor->u.Port.Alignment = length; + Descriptor->u.Port.MinimumAddress.LowPart = *OrigAddress[j] & ~0xF; + Descriptor->u.Port.MaximumAddress.LowPart = + Descriptor->u.Port.MinimumAddress.LowPart + length - 1; + + CompleteList->List[0].Count++; + Descriptor++; + + Descriptor->Flags = Descriptor[-1].Flags; + Descriptor->Option = IO_RESOURCE_ALTERNATIVE; + } + + // + // Add this memory range + // + + Descriptor->Type = CmResourceTypeMemory; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + + Descriptor->u.Memory.Length = length; + Descriptor->u.Memory.Alignment = length; + Descriptor->u.Memory.MaximumAddress.LowPart = m; + + if (memtype == PCI_TYPE_20BIT && m > 0xFFFFF) { + // limit to 20 bit address + Descriptor->u.Memory.MaximumAddress.LowPart = 0xFFFFF; + } + } + + CompleteList->List[0].Count++; + Descriptor++; + + + if (Is64BitBaseAddress(i)) { + // skip upper half of 64 bit address since this processor + // only supports 32 bits of address space + j++; + } + } + } + + CompleteList->ListSize = (ULONG) + ((PUCHAR) Descriptor - (PUCHAR) CompleteList); + + // + // Restore the device settings as we found them, enable memory + // and io decode after setting base addresses. This is done in + // case HalAdjustResourceList wants to read the current settings + // in the device. + // + + HalpWritePCIConfig ( + BusHandler, + PciSlot, + &PciOrigData->Status, + FIELD_OFFSET (PCI_COMMON_CONFIG, Status), + PCI_COMMON_HDR_LENGTH - FIELD_OFFSET (PCI_COMMON_CONFIG, Status) + ); + + HalpWritePCIConfig ( + BusHandler, + PciSlot, + PciOrigData, + 0, + FIELD_OFFSET (PCI_COMMON_CONFIG, Status) + ); + + // + // Have the IO system allocate resource assignments + // + + status = IoAssignResources ( + RegistryPath, + DriverClassName, + DriverObject, + DeviceObject, + CompleteList, + pAllocatedResources + ); + + if (!NT_SUCCESS(status)) { + goto CleanUp; + } + + // + // Slurp the assigments back into the PciData structure and + // perform them + // + + CmDescriptor = (*pAllocatedResources)->List[0].PartialResourceList.PartialDescriptors; + + // + // If PCI device has an interrupt resource then that was + // passed in as the first requested resource + // + + if (RequestedInterrupt) { + PciData->u.type0.InterruptLine = (UCHAR) CmDescriptor->u.Interrupt.Vector; + BusData->CommonData.Line2Pin (BusHandler, RootHandler, PciSlot, PciData, PciOrigData); + CmDescriptor++; + } + + // + // Pull out resources in the order they were passed to IoAssignResources + // + + for (j=0; j < NoBaseAddress; j++) { + i = *BaseAddress[j]; + if (i) { + if (i & PCI_ADDRESS_IO_SPACE) { + *BaseAddress[j] = CmDescriptor->u.Port.Start.LowPart; + } else { + *BaseAddress[j] = CmDescriptor->u.Memory.Start.LowPart; + } + CmDescriptor++; + } + + if (Is64BitBaseAddress(i)) { + // skip upper 32 bits + j++; + } + } + + // + // Turn off decodes, then set new addresses + // + + HalpWritePCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH); + + // + // Read configuration back and verify address settings took + // + + HalpReadPCIConfig(BusHandler, PciSlot, PciData2, 0, PCI_COMMON_HDR_LENGTH); + + Match = TRUE; + if (PciData->u.type0.InterruptLine != PciData2->u.type0.InterruptLine || + PciData->u.type0.InterruptPin != PciData2->u.type0.InterruptPin || + PciData->u.type0.ROMBaseAddress != PciData2->u.type0.ROMBaseAddress) { + Match = FALSE; + } + + for (j=0; j < NoBaseAddress; j++) { + if (*BaseAddress[j]) { + if (*BaseAddress[j] & PCI_ADDRESS_IO_SPACE) { + i = (ULONG) ~0x3; + } else { + i = (ULONG) ~0xF; + } + + if ((*BaseAddress[j] & i) != + *((PULONG) ((PUCHAR) BaseAddress[j] - + (PUCHAR) PciData + + (PUCHAR) PciData2)) & i) { + + Match = FALSE; + } + + if (Is64BitBaseAddress(*BaseAddress[j])) { + // skip upper 32 bits + j++; + } + } + } + + if (!Match) { +#if DBG + DbgPrint ("PCI: defective device! Bus %d, Slot %d, Function %d\n", + BusNumber, + PciSlot.u.bits.DeviceNumber, + PciSlot.u.bits.FunctionNumber + ); +#endif + status = STATUS_DEVICE_PROTOCOL_ERROR; + goto CleanUp; + } + + // + // Settings took - turn on the appropiate decodes + // + + if (EnableRomBase && *BaseAddress[RomIndex]) { + // a rom address was allocated and should be enabled + *BaseAddress[RomIndex] |= PCI_ROMADDRESS_ENABLED; + HalpWritePCIConfig ( + BusHandler, + PciSlot, + BaseAddress[RomIndex], + (ULONG) ((PUCHAR) BaseAddress[RomIndex] - (PUCHAR) PciData), + sizeof (ULONG) + ); + } + + // + // Enable IO, Memory, and BUS_MASTER decodes + // (use HalSetBusData since valid settings now set) + // + + PciData->Command |= PCI_ENABLE_IO_SPACE | + PCI_ENABLE_MEMORY_SPACE | + PCI_ENABLE_BUS_MASTER; + + HalSetBusDataByOffset ( + PCIConfiguration, + BusHandler->BusNumber, + PciSlot.u.AsULONG, + &PciData->Command, + FIELD_OFFSET (PCI_COMMON_CONFIG, Command), + sizeof (PciData->Command) + ); + +CleanUp: + if (!NT_SUCCESS(status)) { + + // + // Failure, if there are any allocated resources free them + // + + if (*pAllocatedResources) { + IoAssignResources ( + RegistryPath, + DriverClassName, + DriverObject, + DeviceObject, + NULL, + NULL + ); + + ExFreePool (*pAllocatedResources); + *pAllocatedResources = NULL; + } + + // + // Restore the device settings as we found them, enable memory + // and io decode after setting base addresses + // + + HalpWritePCIConfig ( + BusHandler, + PciSlot, + &PciOrigData->Status, + FIELD_OFFSET (PCI_COMMON_CONFIG, Status), + PCI_COMMON_HDR_LENGTH - FIELD_OFFSET (PCI_COMMON_CONFIG, Status) + ); + + HalpWritePCIConfig ( + BusHandler, + PciSlot, + PciOrigData, + 0, + FIELD_OFFSET (PCI_COMMON_CONFIG, Status) + ); + } + + ExFreePool (WorkingPool); + return status; +} + +#if DBG +VOID +HalpTestPci (ULONG flag2) +{ + PCI_SLOT_NUMBER SlotNumber; + PCI_COMMON_CONFIG PciData, OrigData; + ULONG i, f, j, k, bus; + BOOLEAN flag; + + + if (!flag2) { + return ; + } + + DbgBreakPoint (); + SlotNumber.u.bits.Reserved = 0; + + // + // Read every possible PCI Device/Function and display it's + // default info. + // + // (note this destories it's current settings) + // + + flag = TRUE; + for (bus = 0; flag; bus++) { + + for (i = 0; i < PCI_MAX_DEVICES; i++) { + SlotNumber.u.bits.DeviceNumber = i; + + for (f = 0; f < PCI_MAX_FUNCTION; f++) { + SlotNumber.u.bits.FunctionNumber = f; + + // + // Note: This is reading the DeviceSpecific area of + // the device's configuration - normally this should + // only be done on device for which the caller understands. + // I'm doing it here only for debugging. + // + + j = HalGetBusData ( + PCIConfiguration, + bus, + SlotNumber.u.AsULONG, + &PciData, + sizeof (PciData) + ); + + if (j == 0) { + // out of buses + flag = FALSE; + break; + } + + if (j < PCI_COMMON_HDR_LENGTH) { + continue; + } + + HalSetBusData ( + PCIConfiguration, + bus, + SlotNumber.u.AsULONG, + &PciData, + 1 + ); + + HalGetBusData ( + PCIConfiguration, + bus, + SlotNumber.u.AsULONG, + &PciData, + sizeof (PciData) + ); + +#if 0 + memcpy (&OrigData, &PciData, sizeof PciData); + + for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { + PciData.u.type0.BaseAddresses[j] = 0xFFFFFFFF; + } + + PciData.u.type0.ROMBaseAddress = 0xFFFFFFFF; + + HalSetBusData ( + PCIConfiguration, + bus, + SlotNumber.u.AsULONG, + &PciData, + sizeof (PciData) + ); + + HalGetBusData ( + PCIConfiguration, + bus, + SlotNumber.u.AsULONG, + &PciData, + sizeof (PciData) + ); +#endif + + DbgPrint ("PCI Bus %d Slot %2d %2d ID:%04lx-%04lx Rev:%04lx", + bus, i, f, PciData.VendorID, PciData.DeviceID, + PciData.RevisionID); + + + if (PciData.u.type0.InterruptPin) { + DbgPrint (" IntPin:%x", PciData.u.type0.InterruptPin); + } + + if (PciData.u.type0.InterruptLine) { + DbgPrint (" IntLine:%x", PciData.u.type0.InterruptLine); + } + + if (PciData.u.type0.ROMBaseAddress) { + DbgPrint (" ROM:%08lx", PciData.u.type0.ROMBaseAddress); + } + + DbgPrint ("\n Cmd:%04x Status:%04x ProgIf:%04x SubClass:%04x BaseClass:%04lx\n", + PciData.Command, PciData.Status, PciData.ProgIf, + PciData.SubClass, PciData.BaseClass); + + k = 0; + for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { + if (PciData.u.type0.BaseAddresses[j]) { + DbgPrint (" Ad%d:%08lx", j, PciData.u.type0.BaseAddresses[j]); + k = 1; + } + } + +#if 0 + if (PciData.u.type0.ROMBaseAddress == 0xC08001) { + + PciData.u.type0.ROMBaseAddress = 0xC00001; + HalSetBusData ( + PCIConfiguration, + bus, + SlotNumber.u.AsULONG, + &PciData, + sizeof (PciData) + ); + + HalGetBusData ( + PCIConfiguration, + bus, + SlotNumber.u.AsULONG, + &PciData, + sizeof (PciData) + ); + + DbgPrint ("\n Bogus rom address, edit yields:%08lx", + PciData.u.type0.ROMBaseAddress); + } +#endif + + if (k) { + DbgPrint ("\n"); + } + + if (PciData.VendorID == 0x8086) { + // dump complete buffer + DbgPrint ("Command %x, Status %x, BIST %x\n", + PciData.Command, PciData.Status, + PciData.BIST + ); + + DbgPrint ("CacheLineSz %x, LatencyTimer %x", + PciData.CacheLineSize, PciData.LatencyTimer + ); + + for (j=0; j < 192; j++) { + if ((j & 0xf) == 0) { + DbgPrint ("\n%02x: ", j + 0x40); + } + DbgPrint ("%02x ", PciData.DeviceSpecific[j]); + } + DbgPrint ("\n"); + } + + +#if 0 + // + // now print original data + // + + if (OrigData.u.type0.ROMBaseAddress) { + DbgPrint (" oROM:%08lx", OrigData.u.type0.ROMBaseAddress); + } + + DbgPrint ("\n"); + k = 0; + for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { + if (OrigData.u.type0.BaseAddresses[j]) { + DbgPrint (" oAd%d:%08lx", j, OrigData.u.type0.BaseAddresses[j]); + k = 1; + } + } + + // + // Restore original settings + // + + HalSetBusData ( + PCIConfiguration, + bus, + SlotNumber.u.AsULONG, + &OrigData, + sizeof (PciData) + ); +#endif + + // + // Next + // + + if (k) { + DbgPrint ("\n\n"); + } + } + } + } + DbgBreakPoint (); +} +#endif diff --git a/private/ntos/nthals/halx86/i386/ixpciint.c b/private/ntos/nthals/halx86/i386/ixpciint.c new file mode 100644 index 000000000..896e141f7 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixpciint.c @@ -0,0 +1,458 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixpciint.c + +Abstract: + + All PCI bus interrupt mapping is in this module, so that a real + system which doesn't have all the limitations which PC PCI + systems have can replaced this code easly. + (bus memory & i/o address mappings can also be fix here) + +Author: + + Ken Reneris + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "halp.h" +#include "pci.h" +#include "pcip.h" + +ULONG PciIsaIrq; +ULONG HalpEisaELCR; +BOOLEAN HalpDoingCrashDump; +BOOLEAN HalpPciLockSettings; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,HalpGetPCIIntOnISABus) +#pragma alloc_text(PAGE,HalpAdjustPCIResourceList) +#pragma alloc_text(PAGE,HalpGetISAFixedPCIIrq) +#endif + + +ULONG +HalpGetPCIIntOnISABus ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ) +{ + if (BusInterruptLevel < 1) { + // bogus bus level + return 0; + } + + + // + // Current PCI buses just map their IRQs ontop of the ISA space, + // so foreward this to the isa handler for the isa vector + // (the isa vector was saved away at either HalSetBusData or + // IoAssignReosurces time - if someone is trying to connect a + // PCI interrupt without performing one of those operations first, + // they are broken). + // + + return HalGetInterruptVector ( +#ifndef MCA + Isa, 0, +#else + MicroChannel, 0, +#endif + BusInterruptLevel ^ IRQXOR, + 0, + Irql, + Affinity + ); +} + + +VOID +HalpPCIPin2ISALine ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciData + ) +/*++ + + This function maps the device's InterruptPin to an InterruptLine + value. + + On the current PC implementations, the bios has already filled in + InterruptLine as it's ISA value and there's no portable way to + change it. + + On a DBG build we adjust InterruptLine just to ensure driver's + don't connect to it without translating it on the PCI bus. + +--*/ +{ + if (!PciData->u.type0.InterruptPin) { + return ; + } + + // + // Set vector as a level vector. (note: this code assumes the + // irq is static and does not move). + // + + if (PciData->u.type0.InterruptLine >= 1 && + PciData->u.type0.InterruptLine <= 15) { + + // + // If this bit was on the in the PIC ELCR register, + // then mark it in PciIsaIrq. (for use in hal.dll, + // such that we can assume the interrupt controller + // has been properly marked as a level interrupt for + // this IRQ. Other hals probabily don't care.) + // + + PciIsaIrq |= HalpEisaELCR & (1 << PciData->u.type0.InterruptLine); + } + + // + // On a PC there's no Slot/Pin/Line mapping which needs to + // be done. + // + + PciData->u.type0.InterruptLine ^= IRQXOR; +} + + + +VOID +HalpPCIISALine2Pin ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciNewData, + IN PPCI_COMMON_CONFIG PciOldData + ) +/*++ + + This functions maps the device's InterruptLine to it's + device specific InterruptPin value. + + On the current PC implementations, this information is + fixed by the BIOS. Just make sure the value isn't being + editted since PCI doesn't tell us how to dynically + connect the interrupt. + +--*/ +{ + if (!PciNewData->u.type0.InterruptPin) { + return ; + } + + PciNewData->u.type0.InterruptLine ^= IRQXOR; + +#if DBG + if (PciNewData->u.type0.InterruptLine != PciOldData->u.type0.InterruptLine || + PciNewData->u.type0.InterruptPin != PciOldData->u.type0.InterruptPin) { + DbgPrint ("HalpPCILine2Pin: System does not support changing the PCI device interrupt routing\n"); + DbgBreakPoint (); + } +#endif +} + +#if !defined(SUBCLASSPCI) + +VOID +HalpPCIAcquireType2Lock ( + PKSPIN_LOCK SpinLock, + PKIRQL Irql + ) +{ + if (!HalpDoingCrashDump) { + *Irql = KfRaiseIrql (HIGH_LEVEL); + KiAcquireSpinLock (SpinLock); + } else { + *Irql = HIGH_LEVEL; + } +} + + +VOID +HalpPCIReleaseType2Lock ( + PKSPIN_LOCK SpinLock, + KIRQL Irql + ) +{ + if (!HalpDoingCrashDump) { + KiReleaseSpinLock (SpinLock); + KfLowerIrql (Irql); + } +} + +#endif + +NTSTATUS +HalpAdjustPCIResourceList ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList + ) +/*++ + Rewrite the callers requested resource list to fit within + the supported ranges of this bus +--*/ +{ + NTSTATUS Status; + PPCIPBUSDATA BusData; + PCI_SLOT_NUMBER PciSlot; + PSUPPORTED_RANGE Interrupt; + PSUPPORTED_RANGE Range; + PSUPPORTED_RANGES SupportedRanges; + PPCI_COMMON_CONFIG PciData, PciOrigData; + UCHAR buffer[PCI_COMMON_HDR_LENGTH]; + UCHAR buffer2[PCI_COMMON_HDR_LENGTH]; + BOOLEAN UseBusRanges; + ULONG i, j, RomIndex, length, ebit; + ULONG Base[PCI_TYPE0_ADDRESSES + 1]; + PULONG BaseAddress[PCI_TYPE0_ADDRESSES + 1]; + + + BusData = (PPCIPBUSDATA) BusHandler->BusData; + PciSlot = *((PPCI_SLOT_NUMBER) &(*pResourceList)->SlotNumber); + + // + // Determine PCI device's interrupt restrictions + // + + Status = BusData->GetIrqRange(BusHandler, RootHandler, PciSlot, &Interrupt); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + SupportedRanges = NULL; + UseBusRanges = TRUE; + Status = STATUS_INSUFFICIENT_RESOURCES; + + if (HalpPciLockSettings) { + + PciData = (PPCI_COMMON_CONFIG) buffer; + PciOrigData = (PPCI_COMMON_CONFIG) buffer2; + HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH); + + // + // If this is a device, and it current has its decodes enabled, + // then use the currently programmed ranges only + // + + if (PCI_CONFIG_TYPE(PciData) == 0 && + (PciData->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE))) { + + // + // Save current settings + // + + RtlMoveMemory (PciOrigData, PciData, PCI_COMMON_HDR_LENGTH); + + + for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { + BaseAddress[j] = &PciData->u.type0.BaseAddresses[j]; + } + BaseAddress[j] = &PciData->u.type0.ROMBaseAddress; + RomIndex = j; + + // + // Write all one-bits to determine lengths for each address + // + + for (j=0; j < PCI_TYPE0_ADDRESSES + 1; j++) { + Base[j] = *BaseAddress[j]; + *BaseAddress[j] = 0xFFFFFFFF; + } + + PciData->Command &= ~(PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE); + *BaseAddress[RomIndex] &= ~PCI_ROMADDRESS_ENABLED; + HalpWritePCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH); + HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH); + + // + // restore original settings + // + + HalpWritePCIConfig ( + BusHandler, + PciSlot, + &PciOrigData->Status, + FIELD_OFFSET (PCI_COMMON_CONFIG, Status), + PCI_COMMON_HDR_LENGTH - FIELD_OFFSET (PCI_COMMON_CONFIG, Status) + ); + + HalpWritePCIConfig ( + BusHandler, + PciSlot, + PciOrigData, + 0, + FIELD_OFFSET (PCI_COMMON_CONFIG, Status) + ); + + // + // Build a memory & io range list of just the ranges already + // programmed into the device + // + + UseBusRanges = FALSE; + SupportedRanges = HalpAllocateNewRangeList(); + if (!SupportedRanges) { + goto CleanUp; + } + + *BaseAddress[RomIndex] &= ~PCI_ADDRESS_IO_SPACE; + for (j=0; j < PCI_TYPE0_ADDRESSES + 1; j++) { + + i = *BaseAddress[j]; + + if (i & PCI_ADDRESS_IO_SPACE) { + length = 1 << 2; + Range = &SupportedRanges->IO; + ebit = PCI_ENABLE_IO_SPACE; + + } else { + length = 1 << 4; + Range = &SupportedRanges->Memory; + ebit = PCI_ENABLE_MEMORY_SPACE; + + if (i & PCI_ADDRESS_MEMORY_PREFETCHABLE) { + Range = &SupportedRanges->PrefetchMemory; + } + } + + Base[j] &= ~(length-1); + while (!(i & length) && length) { + length <<= 1; + } + + if (j == RomIndex && + !(PciOrigData->u.type0.ROMBaseAddress & PCI_ROMADDRESS_ENABLED)) { + + // range not enabled, don't use it + length = 0; + } + + if (length) { + if (!(PciOrigData->Command & ebit)) { + // range not enabled, don't use preprogrammed values + UseBusRanges = TRUE; + } + + if (Range->Limit >= Range->Base) { + Range->Next = ExAllocatePool (PagedPool, sizeof (SUPPORTED_RANGE)); + Range = Range->Next; + if (!Range) { + goto CleanUp; + } + + Range->Next = NULL; + } + + Range->Base = Base[j]; + Range->Limit = Base[j] + length - 1; + } + + if (Is64BitBaseAddress(i)) { + // skip upper half of 64 bit address since this processor + // only supports 32 bits of address space + j++; + } + } + } + } + + // + // Adjust resources + // + + Status = HaliAdjustResourceListRange ( + UseBusRanges ? BusHandler->BusAddresses : SupportedRanges, + Interrupt, + pResourceList + ); + +CleanUp: + if (SupportedRanges) { + HalpFreeRangeList (SupportedRanges); + } + + ExFreePool (Interrupt); + return Status; +} + + + +NTSTATUS +HalpGetISAFixedPCIIrq ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER PciSlot, + OUT PSUPPORTED_RANGE *Interrupt + ) +{ + UCHAR buffer[PCI_COMMON_HDR_LENGTH]; + PPCI_COMMON_CONFIG PciData; + + + PciData = (PPCI_COMMON_CONFIG) buffer; + HalGetBusData ( + PCIConfiguration, + BusHandler->BusNumber, + PciSlot.u.AsULONG, + PciData, + PCI_COMMON_HDR_LENGTH + ); + + if (PciData->VendorID == PCI_INVALID_VENDORID || + PCI_CONFIG_TYPE (PciData) != 0) { + return STATUS_UNSUCCESSFUL; + } + + *Interrupt = ExAllocatePool (PagedPool, sizeof (SUPPORTED_RANGE)); + if (!*Interrupt) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory (*Interrupt, sizeof (SUPPORTED_RANGE)); + (*Interrupt)->Base = 1; // base = 1, limit = 0 + + if (!PciData->u.type0.InterruptPin) { + return STATUS_SUCCESS; + } + + if (PciData->u.type0.InterruptLine == (0 ^ IRQXOR) || + PciData->u.type0.InterruptLine == (0xFF ^ IRQXOR)) { + +#if DBG + DbgPrint ("HalpGetValidPCIFixedIrq: BIOS did not assign an interrupt vector for the device\n"); +#endif + // + // We need to let the caller continue, since the caller may + // not care that the interrupt vector is connected or not + // + + return STATUS_SUCCESS; + } + + (*Interrupt)->Base = PciData->u.type0.InterruptLine; + (*Interrupt)->Limit = PciData->u.type0.InterruptLine; + return STATUS_SUCCESS; +} diff --git a/private/ntos/nthals/halx86/i386/ixphwsup.c b/private/ntos/nthals/halx86/i386/ixphwsup.c new file mode 100644 index 000000000..ce04cdbfb --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixphwsup.c @@ -0,0 +1,535 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ixphwsup.c + +Abstract: + + This module contains the HalpXxx routines for the NT I/O system that + are hardware dependent. Were these routines not hardware dependent, + they would normally reside in the internal.c module. + +Author: + + Darryl E. Havens (darrylh) 11-Apr-1990 + +Environment: + + Kernel mode, local to I/O system + +Revision History: + + +--*/ + +#include "halp.h" +#if MCA + +#include "mca.h" + +#else + +#include "eisa.h" + +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,HalpAllocateAdapter) +#pragma alloc_text(PAGELK,HalpGrowMapBuffers) +#endif + + +// +// Some devices require a phyicially contiguous data buffers for DMA transfers. +// Map registers are used give the appearance that all data buffers are +// contiguous. In order to pool all of the map registers a master +// adapter object is used. This object is allocated and saved internal to this +// file. It contains a bit map for allocation of the registers and a queue +// for requests which are waiting for more map registers. This object is +// allocated during the first request to allocate an adapter which requires +// map registers. +// + +PADAPTER_OBJECT MasterAdapterObject; + +BOOLEAN LessThan16Mb; +BOOLEAN HalpEisaDma; +// +// Map buffer prameters. These are initialized in HalInitSystem +// + +PHYSICAL_ADDRESS HalpMapBufferPhysicalAddress; +ULONG HalpMapBufferSize; + +BOOLEAN +HalpGrowMapBuffers( + PADAPTER_OBJECT AdapterObject, + ULONG Amount + ) +/*++ + +Routine Description: + + This function attempts to allocate additional map buffers for use by I/O + devices. The map register table is updated to indicate the additional + buffers. + + Caller owns the HalpNewAdapter event + +Arguments: + + AdapterObject - Supplies the adapter object for which the buffers are to be + allocated. + + Amount - Indicates the size of the map buffers which should be allocated. + +Return Value: + + TRUE is returned if the memory could be allocated. + + FALSE is returned if the memory could not be allocated. + +--*/ +{ + ULONG MapBufferPhysicalAddress; + PVOID MapBufferVirtualAddress; + PTRANSLATION_ENTRY TranslationEntry; + LONG NumberOfPages; + LONG i; + PHYSICAL_ADDRESS physicalAddress; + KIRQL Irql; + PVOID CodeLockHandle; + + PAGED_CODE(); + + NumberOfPages = BYTES_TO_PAGES(Amount); + + // + // Make sure there is room for the addition pages. The maximum number of + // slots needed is equal to NumberOfPages + Amount / 64K + 1. + // + + i = BYTES_TO_PAGES(MAXIMUM_MAP_BUFFER_SIZE) - (NumberOfPages + + (NumberOfPages * PAGE_SIZE) / 0x10000 + 1 + + AdapterObject->NumberOfMapRegisters); + + if (i < 0) { + + // + // Reduce the allocatation amount to so it will fit. + // + + NumberOfPages += i; + } + + if (NumberOfPages <= 0) { + + // + // No more memory can be allocated. + // + + return(FALSE); + } + + if (AdapterObject->NumberOfMapRegisters == 0 && HalpMapBufferSize) { + + NumberOfPages = BYTES_TO_PAGES(HalpMapBufferSize); + + // + // Since this is the initial allocation, use the buffer allocated by + // HalInitSystem rather than allocationg a new one. + // + + MapBufferPhysicalAddress = HalpMapBufferPhysicalAddress.LowPart; + + // + // Map the buffer for access. + // + + MapBufferVirtualAddress = MmMapIoSpace( + HalpMapBufferPhysicalAddress, + HalpMapBufferSize, + TRUE // Cache enable. + ); + + if (MapBufferVirtualAddress == NULL) { + + // + // The buffer could not be mapped. + // + + HalpMapBufferSize = 0; + return(FALSE); + } + + + } else { + + // + // Allocate the map buffers. + // + + physicalAddress.LowPart = MAXIMUM_PHYSICAL_ADDRESS - 1; + physicalAddress.HighPart = 0; + + MapBufferVirtualAddress = MmAllocateContiguousMemory( + NumberOfPages * PAGE_SIZE, + physicalAddress + ); + + if (MapBufferVirtualAddress == NULL) { + return(FALSE); + } + + // + // Get the physical address of the map base. + // + + MapBufferPhysicalAddress = MmGetPhysicalAddress( + MapBufferVirtualAddress + ).LowPart; + + } + + // + // Initailize the map registers where memory has been allocated. + // Serialize with master adapter object + // + + CodeLockHandle = MmLockPagableCodeSection (&HalpGrowMapBuffers); + Irql = KfAcquireSpinLock( &AdapterObject->SpinLock ); + + TranslationEntry = ((PTRANSLATION_ENTRY) AdapterObject->MapRegisterBase) + + AdapterObject->NumberOfMapRegisters; + + for (i = 0; (LONG) i < NumberOfPages; i++) { + + // + // Make sure the perivous entry is physically contiguous with the next + // entry and that a 64K physical bountry is not crossed unless this + // is an Eisa system. + // + + if (TranslationEntry != AdapterObject->MapRegisterBase && + (((TranslationEntry - 1)->PhysicalAddress + PAGE_SIZE) != + MapBufferPhysicalAddress || (!HalpEisaDma && + ((TranslationEntry - 1)->PhysicalAddress & ~0x0ffff) != + (MapBufferPhysicalAddress & ~0x0ffff)))) { + + // + // An entry needs to be skipped in the table. This entry will + // remain marked as allocated so that no allocation of map + // registers will cross this bountry. + // + + TranslationEntry++; + AdapterObject->NumberOfMapRegisters++; + } + + // + // Clear the bits where the memory has been allocated. + // + + RtlClearBits( + AdapterObject->MapRegisters, + TranslationEntry - (PTRANSLATION_ENTRY) + AdapterObject->MapRegisterBase, + 1 + ); + + TranslationEntry->VirtualAddress = MapBufferVirtualAddress; + TranslationEntry->PhysicalAddress = MapBufferPhysicalAddress; + TranslationEntry++; + (PCCHAR) MapBufferVirtualAddress += PAGE_SIZE; + MapBufferPhysicalAddress += PAGE_SIZE; + + } + + // + // Remember the number of pages that where allocated. + // + + AdapterObject->NumberOfMapRegisters += NumberOfPages; + + // + // Release master adapter object + // + + KfReleaseSpinLock( &AdapterObject->SpinLock, Irql ); + MmUnlockPagableImageSection (CodeLockHandle); + return(TRUE); +} + +PADAPTER_OBJECT +HalpAllocateAdapter( + IN ULONG MapRegistersPerChannel, + IN PVOID AdapterBaseVa, + IN PVOID ChannelNumber + ) + +/*++ + +Routine Description: + + This routine allocates and initializes an adapter object to represent an + adapter or a DMA controller on the system. If no map registers are required + then a standalone adapter object is allocated with no master adapter. + + If map registers are required, then a master adapter object is used to + allocate the map registers. For Isa systems these registers are really + phyically contiguous memory pages. + + Caller owns the HalpNewAdapter event + + +Arguments: + + MapRegistersPerChannel - Specifies the number of map registers that each + channel provides for I/O memory mapping. + + AdapterBaseVa - Address of the the DMA controller. + + ChannelNumber - Unused. + +Return Value: + + The function value is a pointer to the allocate adapter object. + +--*/ + +{ + + PADAPTER_OBJECT AdapterObject; + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG Size; + ULONG BitmapSize; + HANDLE Handle; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(ChannelNumber); + + PAGED_CODE(); + + // + // Initalize the master adapter if necessary. + // + + if (MasterAdapterObject == NULL && AdapterBaseVa != (PVOID) -1 && + MapRegistersPerChannel) { + + MasterAdapterObject = HalpAllocateAdapter( + MapRegistersPerChannel, + (PVOID) -1, + NULL + ); + + // + // If we could not allocate the master adapter then give up. + // + + if (MasterAdapterObject == NULL) { + return(NULL); + } + } + + // + // Begin by initializing the object attributes structure to be used when + // creating the adapter object. + // + + InitializeObjectAttributes( &ObjectAttributes, + NULL, + OBJ_PERMANENT, + (HANDLE) NULL, + (PSECURITY_DESCRIPTOR) NULL + ); + + // + // Determine the size of the adapter object. If this is the master object + // then allocate space for the register bit map; otherwise, just allocate + // an adapter object. + // + if (AdapterBaseVa == (PVOID) -1) { + + // + // Allocate a bit map large enough MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE + // of map register buffers. + // + + BitmapSize = (((sizeof( RTL_BITMAP ) + + (( MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE ) + 7 >> 3)) + 3) & ~3); + + Size = sizeof( ADAPTER_OBJECT ) + BitmapSize; + + } else { + + Size = sizeof( ADAPTER_OBJECT ); + + } + + // + // Now create the adapter object. + // + + Status = ObCreateObject( KernelMode, + *IoAdapterObjectType, + &ObjectAttributes, + KernelMode, + (PVOID) NULL, + Size, + 0, + 0, + (PVOID *)&AdapterObject ); + + // + // Reference the object. + // + + if (NT_SUCCESS(Status)) { + + Status = ObReferenceObjectByPointer( + AdapterObject, + FILE_READ_DATA | FILE_WRITE_DATA, + *IoAdapterObjectType, + KernelMode + ); + + } + + // + // If the adapter object was successfully created, then attempt to insert + // it into the the object table. + // + + if (NT_SUCCESS( Status )) { + + RtlZeroMemory (AdapterObject, sizeof (ADAPTER_OBJECT)); + + Status = ObInsertObject( AdapterObject, + NULL, + FILE_READ_DATA | FILE_WRITE_DATA, + 0, + (PVOID *) NULL, + &Handle ); + + if (NT_SUCCESS( Status )) { + + ZwClose( Handle ); + + // + // Initialize the adapter object itself. + // + + AdapterObject->Type = IO_TYPE_ADAPTER; + AdapterObject->Size = (USHORT) Size; + AdapterObject->MapRegistersPerChannel = 1; + AdapterObject->AdapterBaseVa = AdapterBaseVa; + + if (MapRegistersPerChannel) { + + AdapterObject->MasterAdapter = MasterAdapterObject; + + } else { + + AdapterObject->MasterAdapter = NULL; + + } + + // + // Initialize the channel wait queue for this + // adapter. + // + + KeInitializeDeviceQueue( &AdapterObject->ChannelWaitQueue ); + + // + // If this is the MasterAdatper then initialize the register bit map, + // AdapterQueue and the spin lock. + // + + if ( AdapterBaseVa == (PVOID) -1 ) { + + KeInitializeSpinLock( &AdapterObject->SpinLock ); + + InitializeListHead( &AdapterObject->AdapterQueue ); + + AdapterObject->MapRegisters = (PVOID) ( AdapterObject + 1); + + RtlInitializeBitMap( AdapterObject->MapRegisters, + (PULONG) (((PCHAR) (AdapterObject->MapRegisters)) + sizeof( RTL_BITMAP )), + ( MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE ) + ); + // + // Set all the bits in the memory to indicate that memory + // has not been allocated for the map buffers + // + + RtlSetAllBits( AdapterObject->MapRegisters ); + AdapterObject->NumberOfMapRegisters = 0; + AdapterObject->CommittedMapRegisters = 0; + + // + // ALlocate the memory map registers. + // + + AdapterObject->MapRegisterBase = ExAllocatePool( + NonPagedPool, + (MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE) * + sizeof(TRANSLATION_ENTRY) + ); + + if (AdapterObject->MapRegisterBase == NULL) { + + ObDereferenceObject( AdapterObject ); + AdapterObject = NULL; + return(NULL); + + } + + // + // Zero the map registers. + // + + RtlZeroMemory( + AdapterObject->MapRegisterBase, + (MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE) * + sizeof(TRANSLATION_ENTRY) + ); + + if (!HalpGrowMapBuffers(AdapterObject, INITIAL_MAP_BUFFER_SMALL_SIZE)) + { + + // + // If no map registers could be allocated then free the + // object. + // + + ObDereferenceObject( AdapterObject ); + AdapterObject = NULL; + return(NULL); + + } + } + + } else { + + // + // An error was incurred for some reason. Set the return value + // to NULL. + // + + AdapterObject = (PADAPTER_OBJECT) NULL; + } + } else { + + AdapterObject = (PADAPTER_OBJECT) NULL; + + } + + + return AdapterObject; + +} diff --git a/private/ntos/nthals/halx86/i386/ixproc.c b/private/ntos/nthals/halx86/i386/ixproc.c new file mode 100644 index 000000000..c9782e366 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixproc.c @@ -0,0 +1,160 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ixsproc.c + +Abstract: + + Stub functions for UP hals. + +Author: + + Ken Reneris (kenr) 22-Jan-1991 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "halp.h" + +UCHAR HalName[] = "PC Compatible Eisa/Isa HAL"; + +BOOLEAN +HalpInitMP ( + IN ULONG Phase, + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ); + +VOID +HalpMcaInit ( + VOID + ); + +VOID HalpInitOtherBuses (VOID); +VOID HalpInitializePciBus (VOID); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,HalpInitMP) +#pragma alloc_text(INIT,HalStartNextProcessor) +#pragma alloc_text(INIT,HalAllProcessorsStarted) +#pragma alloc_text(INIT,HalReportResourceUsage) +#pragma alloc_text(INIT,HalReportResourceUsage) +#pragma alloc_text(INIT,HalpInitOtherBuses) +#endif + + + +BOOLEAN +HalpInitMP ( + IN ULONG Phase, + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ) +{ + // do nothing + return TRUE; +} + + +VOID +HalpResetAllProcessors ( + VOID + ) +{ + // Just return, that will invoke the standard PC reboot code +} + + +BOOLEAN +HalStartNextProcessor ( + IN PLOADER_PARAMETER_BLOCK pLoaderBlock, + IN PKPROCESSOR_STATE pProcessorState + ) +{ + // no other processors + return FALSE; +} + +BOOLEAN +HalAllProcessorsStarted ( + VOID + ) +{ + // do nothing + return TRUE; +} + + +VOID +HalReportResourceUsage ( + VOID + ) +{ + INTERFACE_TYPE interfacetype; + ANSI_STRING AHalName; + UNICODE_STRING UHalName; + + HalInitSystemPhase2 (); + + switch (HalpBusType) { + case MACHINE_TYPE_ISA: interfacetype = Isa; break; + case MACHINE_TYPE_EISA: interfacetype = Eisa; break; + case MACHINE_TYPE_MCA: interfacetype = MicroChannel; break; + default: interfacetype = Internal; break; + } + + RtlInitAnsiString (&AHalName, HalName); + RtlAnsiStringToUnicodeString (&UHalName, &AHalName, TRUE); + HalpReportResourceUsage ( + &UHalName, // descriptive name + interfacetype // device space interface type + ); + + RtlFreeUnicodeString (&UHalName); + + // + // Turn on MCA support if present + // + + HalpMcaInit(); + + // + // Registry is now intialized, see if there are any PCI buses + // + + HalpInitializePciBus (); + +#if 0 + // + // Display all buses & ranges + // + + HalpDisplayAllBusRanges (); +#endif +} + + +VOID +HalpInitOtherBuses ( + VOID + ) +{ + // no other internal buses supported +} + +ULONG +FASTCALL +HalSystemVectorDispatchEntry ( + IN ULONG Vector, + OUT PKINTERRUPT_ROUTINE **FlatDispatch, + OUT PKINTERRUPT_ROUTINE *NoConnection + ) +{ + return FALSE; +} diff --git a/private/ntos/nthals/halx86/i386/ixprofil.asm b/private/ntos/nthals/halx86/i386/ixprofil.asm new file mode 100644 index 000000000..5758de5d9 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixprofil.asm @@ -0,0 +1,437 @@ + + title "Interval Clock Interrupt" +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; ixprofile.asm +; +; Abstract: +; +; This module implements the code necessary to initialize, +; field and process the profile interrupt. +; +; Author: +; +; Shie-Lin Tzong (shielint) 12-Jan-1990 +; +; Environment: +; +; Kernel mode only. +; +; Revision History: +; +; bryanwi 20-Sep-90 +; +; Add KiSetProfileInterval, KiStartProfileInterrupt, +; KiStopProfileInterrupt procedures. +; KiProfileInterrupt ISR. +; KiProfileList, KiProfileLock are delcared here. +; +; shielint 10-Dec-90 +; Add performance counter support. +; Move system clock to irq8, ie we now use RTC to generate system +; clock. Performance count and Profile use timer 1 counter 0. +; The interval of the irq0 interrupt can be changed by +; KiSetProfileInterval. Performance counter does not care about the +; interval of the interrupt as long as it knows the rollover count. +; Note: Currently I implemented 1 performance counter for the whole +; i386 NT. +; +; John Vert (jvert) 11-Jul-1991 +; Moved from ke\i386 to hal\i386. Removed non-HAL stuff +; +; shie-lin tzong (shielint) 13-March-92 +; Move System clock back to irq0 and use RTC (irq8) to generate +; profile interrupt. Performance counter and system clock use time1 +; counter 0 of 8254. +; +; Landy Wang (landy@corollary.com) 26-Mar-1992 +; Move much code into separate modules for easy inclusion by various +; HAL builds. +; +; Add HalBeginSystemInterrupt() call at beginning of ProfileInterrupt +; code - this must be done before any sti. +; Also add HalpProfileInterrupt2ndEntry for additional processors to +; join the flow of things. +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include mac386.inc +include i386\ix8259.inc +include i386\ixcmos.inc + .list + + EXTRNP _DbgBreakPoint,0,IMPORT + EXTRNP _KeProfileInterrupt,1,IMPORT + EXTRNP Kei386EoiHelper,0,IMPORT + EXTRNP _HalEndSystemInterrupt,2 + EXTRNP _HalBeginSystemInterrupt,3 + EXTRNP _HalpAcquireCmosSpinLock ,0 + EXTRNP _HalpReleaseCmosSpinLock ,0 + +; +; Constants used to initialize CMOS/Real Time Clock +; + +D_INT032 EQU 8E00h ; access word for 386 ring 0 interrupt gate +REGISTER_B_ENABLE_PERIODIC_INTERRUPT EQU 01000010B + ; RT/CMOS Register 'B' Init byte + ; Values for byte shown are + ; Bit 7 = Update inhibit + ; Bit 6 = Periodic interrupt enable + ; Bit 5 = Alarm interrupt disable + ; Bit 4 = Update interrupt disable + ; Bit 3 = Square wave disable + ; Bit 2 = BCD data format + ; Bit 1 = 24 hour time mode + ; Bit 0 = Daylight Savings disable + +REGISTER_B_DISABLE_PERIODIC_INTERRUPT EQU 00000010B + +_DATA SEGMENT DWORD PUBLIC 'DATA' + +RegisterAProfileValue db 00101000B ; default interval = 3.90625 ms + +align 4 +ProfileIntervalTable dd 1221 ; unit = 100 ns + dd 2441 + dd 4883 + dd 9766 + dd 19531 + dd 39063 + dd 78125 + dd 156250 + dd 312500 + dd 625000 + dd 1250000 + dd 2500000 + dd 5000000 + dd 5000000 OR 80000000H + +ProfileIntervalInitTable db 00100011B + db 00100100B + db 00100101B + db 00100110B + db 00100111B + db 00101000B + db 00101001B + db 00101010B + db 00101011B + db 00101100B + db 00101101B + db 00101110B + db 00101111B + db 00101111B + +; +; The following array stores the per microsecond loop count for each +; central processor. +; + +HalpProfileInterval dd -1 +HalpProfilingStopped dd 1 + +; +; HALs wishing to reuse the code in this module should set the HAL +; global variable IxProfileVector to their profile vector. +; + public _IxProfileVector +_IxProfileVector dd PROFILE_VECTOR + +_DATA ends + + +_TEXT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; HalStartProfileInterrupt( +; IN ULONG Reserved +; ); +; +; Routine Description: +; +; What we do here is change the interrupt +; rate from the slowest thing we can get away with to the value +; that's been KeSetProfileInterval +; +; All processors will run this routine, but it doesn't hurt to have +; each one reinitialize the CMOS, since none of them will be let go +; from the stall until they all finish. +; +;-- + +cPublicProc _HalStartProfileInterrupt ,1 + +; Mark profiling as active +; + + mov dword ptr HalpProfilingStopped, 0 + +; +; Set the interrupt rate to what is actually needed +; + stdCall _HalpAcquireCmosSpinLock ; intr disabled + + mov al, RegisterAProfileValue + shl ax, 8 + mov al, 0AH ; Register A + CMOS_WRITE ; Initialize it +; +; Don't clobber the Daylight Savings Time bit in register B, because we +; stash the LastKnownGood "environment variable" there. +; + mov ax, 0bh + CMOS_READ + and al, 1 + mov ah, al + or ah, REGISTER_B_ENABLE_PERIODIC_INTERRUPT + mov al, 0bh + CMOS_WRITE ; Initialize it + mov al,0CH ; Register C + CMOS_READ ; Read to initialize + mov al,0DH ; Register D + CMOS_READ ; Read to initialize + + stdCall _HalpReleaseCmosSpinLock + + stdRET _HalStartProfileInterrupt + +stdENDP _HalStartProfileInterrupt + + + +;++ +; +; HalStopProfileInterrupt( +; IN ULONG Reserved +; ); +; +; Routine Description: +; +; What we do here is change the interrupt +; rate from the high profiling rate to the slowest thing we +; can get away with for PerformanceCounter rollover notification. +; +;-- + +cPublicProc _HalStopProfileInterrupt ,1 + +; +; Turn off profiling hit computation and profile interrupt +; + +; +; Don't clobber the Daylight Savings Time bit in register B, because we +; stash the LastKnownGood "environment variable" there. + + stdCall _HalpAcquireCmosSpinLock ; intr disabled + mov ax, 0bh + CMOS_READ + and al, 1 + mov ah, al + or ah, REGISTER_B_DISABLE_PERIODIC_INTERRUPT + mov al, 0bh + CMOS_WRITE ; Initialize it + mov al,0CH ; Register C + CMOS_READ ; dismiss pending profiling interrupt + mov dword ptr HalpProfilingStopped, 1 + stdCall _HalpReleaseCmosSpinLock + + stdRET _HalStopProfileInterrupt + +stdENDP _HalStopProfileInterrupt + +;++ +; ULONG +; HalSetProfileInterval ( +; ULONG Interval +; ); +; +; Routine Description: +; +; This procedure sets the interrupt rate (and thus the sampling +; interval) for the profiling interrupt. +; +; If profiling is active (KiProfilingStopped == 0) the actual +; hardware interrupt rate will be set. Otherwise, a simple +; rate validation computation is done. +; +; Arguments: +; +; (TOS+4) - Interval in 100ns unit. +; +; Return Value: +; +; Interval actually used by system. +; +;-- + +cPublicProc _HalSetProfileInterval ,1 + + mov edx, [esp+4] ; [edx] = interval in 100ns unit + and edx, 7FFFFFFFh ; Remove highest bit. + mov ecx, 0 ; index = 0 + +Hspi00: + mov eax, ProfileIntervalTable[ecx * 4] + cmp edx, eax ; if request interval < suport interval + jbe short Hspi10 ; if be, find supported interval + inc ecx + jmp short Hspi00 + +Hspi10: + and eax, 7FFFFFFFh ; remove highest bit from supported interval + jecxz short Hspi20 ; If first entry then use it + + push esi ; See which is closer to requested + mov esi, eax ; rate - current entry, or preceeding + sub esi, edx + + sub edx, ProfileIntervalTable[ecx * 4 - 4] + cmp esi, edx + pop esi + jc short Hspi20 + + dec ecx ; use preceeding entry + mov eax, ProfileIntervalTable[ecx * 4] + +Hspi20: + push eax ; save interval value + mov al, ProfileIntervalInitTable[ecx] + mov RegisterAProfileValue, al + test dword ptr HalpProfilingStopped,-1 + jnz short Hspi90 + + stdCall _HalStartProfileInterrupt,<0> ; Re-start profile interrupt + ; with the new interval + +Hspi90: pop eax + stdRET _HalSetProfileInterval ; (eax) = cReturn interval + +stdENDP _HalSetProfileInterval + + page ,132 + subttl "System Profile Interrupt" +;++ +; +; Routine Description: +; +; This routine is entered as the result of a profile interrupt. +; Its function is to dismiss the interrupt, raise system Irql to +; PROFILE_LEVEL and transfer control to +; the standard system routine to process any active profiles. +; +; Arguments: +; +; None +; Interrupt is disabled +; +; Return Value: +; +; Does not return, jumps directly to KeProfileInterrupt, which returns +; +; Sets Irql = PROFILE_LEVEL and dismisses the interrupt +; +;-- + ENTER_DR_ASSIST Hpi_a, Hpi_t + +cPublicProc _HalpProfileInterrupt ,0 + +; +; Save machine state in trap frame +; + + ENTER_INTERRUPT Hpi_a, Hpi_t + +; +; (esp) - base of trap frame +; +; HalBeginSystemInterrupt must be called before any sti's +; +; + + push _IxProfileVector + sub esp, 4 ; allocate space to save OldIrql + stdCall _HalBeginSystemInterrupt, + + or al,al ; check for spurious interrupt + jz short Hpi100 + + +; +; This is the RTC interrupt, so we have to clear the +; interrupt flag on the RTC. +; + stdCall _HalpAcquireCmosSpinLock + +; +; clear interrupt flag on RTC by banging on the CMOS. On some systems this +; doesn't work the first time we do it, so we do it twice. It is rumored that +; some machines require more than this, but that hasn't been observed with NT. +; + + mov al,0CH ; Register C + CMOS_READ ; Read to initialize + mov al,0CH ; Register C + CMOS_READ ; Read to initialize +if DBG + align 4 +Hpi10: test al, 80h + jz short Hpi15 + mov al,0CH ; Register C + CMOS_READ ; Read to initialize + jmp short Hpi10 +Hpi15: +endif ; DBG + + stdCall _HalpReleaseCmosSpinLock + + sti +; +; This entry point is provided for symmetric multiprocessor HALs. +; Since it only makes sense for one processor to clear the CMOS, +; all other processors can instead jmp into this entry point. +; + + align 4 + public _HalpProfileInterrupt2ndEntry@0 +_HalpProfileInterrupt2ndEntry@0: + +; +; (esp) = OldIrql +; (esp+4) = H/W vector +; (esp+8) = base of trap frame +; + +; +; Now check for any profiling stuff to do. +; + + cmp HalpProfilingStopped, dword ptr 1 ; Has profiling been stopped? + jz short Hpi90 ; if z, prof disenabled + + stdCall _KeProfileInterrupt, ; (ebp) = trapframe + +Hpi90: + INTERRUPT_EXIT + + align 4 +Hpi100: + add esp, 8 ; spurious, no EndOfInterrupt + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi + +stdENDP _HalpProfileInterrupt + +_TEXT ends + + end diff --git a/private/ntos/nthals/halx86/i386/ixreboot.c b/private/ntos/nthals/halx86/i386/ixreboot.c new file mode 100644 index 000000000..a310dd73d --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixreboot.c @@ -0,0 +1,143 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ixreboot.c + +Abstract: + + Provides the interface to the firmware for x86. Since there is no + firmware to speak of on x86, this is just reboot support. + +Author: + + John Vert (jvert) 12-Aug-1991 + +Revision History: + +--*/ +#include "halp.h" + +// +// Defines to let us diddle the CMOS clock and the keyboard +// + +#define CMOS_CTRL (PUCHAR )0x70 +#define CMOS_DATA (PUCHAR )0x71 + +#define RESET 0xfe +#define KEYBPORT (PUCHAR )0x64 + +// +// Private function prototypes +// + +VOID +HalpReboot ( + VOID + ); + +VOID +HalpReboot ( + VOID + ) + +/*++ + +Routine Description: + + This procedure resets the CMOS clock to the standard timer settings + so the bios will work, and then issues a reset command to the keyboard + to cause a warm boot. + + It is very machine dependent, this implementation is intended for + PC-AT like machines. + + This code copied from the "old debugger" sources. + + N.B. + + Will NOT return. + +--*/ + +{ + UCHAR Scratch; + PUSHORT Magic; + + // + // By sticking 0x1234 at physical location 0x472, we can bypass the + // memory check after a reboot. + // + + Magic = HalpMapPhysicalMemory(0, 1); + Magic[0x472 / sizeof(USHORT)] = 0x1234; + + // + // Turn off interrupts + // + + HalpAcquireCmosSpinLock(); + + _asm { + cli + } + + // + // Reset the cmos clock to a standard value + // (We are setting the periodic interrupt control on the MC147818) + // + + // + // Disable periodic interrupt + // + + WRITE_PORT_UCHAR(CMOS_CTRL, 0x0b); // Set up for control reg B. + KeStallExecutionProcessor(1); + + Scratch = READ_PORT_UCHAR(CMOS_DATA); + KeStallExecutionProcessor(1); + + Scratch &= 0xbf; // Clear periodic interrupt enable + + WRITE_PORT_UCHAR(CMOS_DATA, Scratch); + KeStallExecutionProcessor(1); + + // + // Set "standard" divider rate + // + + WRITE_PORT_UCHAR(CMOS_CTRL, 0x0a); // Set up for control reg A. + KeStallExecutionProcessor(1); + + Scratch = READ_PORT_UCHAR(CMOS_DATA); + KeStallExecutionProcessor(1); + + Scratch &= 0xf0; // Clear rate setting + Scratch |= 6; // Set default rate and divider + + WRITE_PORT_UCHAR(CMOS_DATA, Scratch); + KeStallExecutionProcessor(1); + + // + // Set a "neutral" cmos address to prevent weirdness + // (Why is this needed? Source this was copied from doesn't say) + // + + WRITE_PORT_UCHAR(CMOS_CTRL, 0x15); + KeStallExecutionProcessor(1); + + HalpResetAllProcessors(); + + // + // If we return, send the reset command to the keyboard controller + // + + WRITE_PORT_UCHAR(KEYBPORT, RESET); + + _asm { + hlt + } +} diff --git a/private/ntos/nthals/halx86/i386/ixstall.asm b/private/ntos/nthals/halx86/i386/ixstall.asm new file mode 100644 index 000000000..3bc27318c --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixstall.asm @@ -0,0 +1,480 @@ + + title "Stall Execution Support" +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; ixstall.asm +; +; Abstract: +; +; This module implements the code necessary to field and process the +; interval clock interrupt. +; +; Author: +; +; Shie-Lin Tzong (shielint) 12-Jan-1990 +; +; Environment: +; +; Kernel mode only. +; +; Revision History: +; +; bryanwi 20-Sep-90 +; +; Add KiSetProfileInterval, KiStartProfileInterrupt, +; KiStopProfileInterrupt procedures. +; KiProfileInterrupt ISR. +; KiProfileList, KiProfileLock are delcared here. +; +; shielint 10-Dec-90 +; Add performance counter support. +; Move system clock to irq8, ie we now use RTC to generate system +; clock. Performance count and Profile use timer 1 counter 0. +; The interval of the irq0 interrupt can be changed by +; KiSetProfileInterval. Performance counter does not care about the +; interval of the interrupt as long as it knows the rollover count. +; Note: Currently I implemented 1 performance counter for the whole +; i386 NT. +; +; John Vert (jvert) 11-Jul-1991 +; Moved from ke\i386 to hal\i386. Removed non-HAL stuff +; +; shie-lin tzong (shielint) 13-March-92 +; Move System clock back to irq0 and use RTC (irq8) to generate +; profile interrupt. Performance counter and system clock use time1 +; counter 0 of 8254. +; +; Landy Wang (corollary!landy) 04-Dec-92 +; Created this module by moving routines from ixclock.asm to here. +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\ix8259.inc +include i386\kimacro.inc +include mac386.inc +include i386\ixcmos.inc + .list + + EXTRNP _DbgBreakPoint,0,IMPORT + EXTRNP _HalpAcquireCmosSpinLock ,0 + EXTRNP _HalpReleaseCmosSpinLock ,0 + +; +; Constants used to initialize CMOS/Real Time Clock +; + +D_INT032 EQU 8E00h ; access word for 386 ring 0 interrupt gate +RTCIRQ EQU 8 ; IRQ number for RTC interrupt +REGISTER_B_ENABLE_PERIODIC_INTERRUPT EQU 01000010B + ; RT/CMOS Register 'B' Init byte + ; Values for byte shown are + ; Bit 7 = Update inhibit + ; Bit 6 = Periodic interrupt enable + ; Bit 5 = Alarm interrupt disable + ; Bit 4 = Update interrupt disable + ; Bit 3 = Square wave disable + ; Bit 2 = BCD data format + ; Bit 1 = 24 hour time mode + ; Bit 0 = Daylight Savings disable + +REGISTER_B_DISABLE_PERIODIC_INTERRUPT EQU 00000010B + +; +; RegisterAInitByte sets 8Hz clock rate, used during init to set up +; KeStallExecutionProcessor, etc. (See RegASystemClockByte below.) +; + +RegisterAInitByte EQU 00101101B ; RT/CMOS Register 'A' init byte + ; 32.768KHz Base divider rate + ; 8Hz int rate, period = 125.0ms +PeriodInMicroSecond EQU 125000 ; + +_DATA SEGMENT DWORD PUBLIC 'DATA' + +HalpP0BugBugStallCount dd 0 + +_DATA ends + +INIT SEGMENT PARA PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "Initialize Stall Execution Counter" +;++ +; +; VOID +; HalpInitializeStallExecution ( +; IN CCHAR ProcessorNumber +; ) +; +; Routine Description: +; +; This routine initialize the per Microsecond counter for +; KeStallExecutionProcessor +; +; Arguments: +; +; ProcessorNumber - Processor Number +; +; Return Value: +; +; None. +; +; Note: +; +; Current implementation assumes that all the processors share +; the same Real Time Clock. So, the dispatcher database lock is +; acquired before entering this routine to guarantee only one +; processor can access the routine. +; +;-- + +KiseInterruptCount equ [ebp-12] ; local variable + +cPublicProc _HalpInitializeStallExecution ,1 + +ifndef NT_UP +;; +;; This function currently doesn't work from any processor but the +;; boot processor - for now stub out the others +;; + + mov eax, PCR[PcPrcb] + cmp byte ptr [eax].PbNumber, 0 + je @f + + mov eax, HalpP0BugBugStallCount + mov PCR[PcStallScaleFactor], eax + stdRET _HalpInitializeStallExecution +@@: +endif + + push ebp ; save ebp + mov ebp, esp ; set up 12 bytes for local use + sub esp, 12 + + pushfd ; save caller's eflag + +; +; Initialize Real Time Clock to interrupt us for every 125ms at +; IRQ 8. +; + + cli ; make sure interrupts are disabled + +; +; Get and save current 8259 masks +; + + xor eax,eax + +; +; Assume there is no third and fourth PICs +; +; Get interrupt Mask on PIC2 +; + + in al,PIC2_PORT1 + shl eax, 8 + +; +; Get interrupt Mask on PIC1 +; + + in al,PIC1_PORT1 + push eax ; save the masks + mov eax, NOT (( 1 SHL PIC_SLAVE_IRQ) + (1 SHL RTCIRQ)) + ; Mask all the irqs except irq 2 and 8 + SET_8259_MASK ; Set 8259's int mask register + +; +; Since RTC interrupt will come from IRQ 8, we need to +; Save original irq 8 descriptor and set the descriptor to point to +; our own handler. +; + + sidt fword ptr [ebp-8] ; get IDT address + mov ecx, [ebp-6] ; (ecx)->IDT + + mov eax, (RTCIRQ+PRIMARY_VECTOR_BASE) + + shl eax, 3 ; 8 bytes per IDT entry + add ecx, eax ; now at the correct IDT RTC entry + + push dword ptr [ecx] ; (TOS) = original desc of IRQ 8 + push dword ptr [ecx + 4] ; each descriptor has 8 bytes + + ; + ; Pushing the appropriate entry address now (instead of + ; the IDT start address later) to make the pop at the end simpler. + ; + push ecx ; (TOS) -> &IDT[HalProfileVector] + + mov eax, offset FLAT:RealTimeClockHandler + + mov word ptr [ecx], ax ; Lower half of handler addr + mov word ptr [ecx+2], KGDT_R0_CODE ; set up selector + mov word ptr [ecx+4], D_INT032 ; 386 interrupt gate + + shr eax, 16 ; (ax)=higher half of handler addr + mov word ptr [ecx+6], ax + + mov dword ptr KiseinterruptCount, 0 ; set no interrupt yet + + stdCall _HalpAcquireCmosSpinLock ; intr disabled + + mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A + CMOS_WRITE ; Initialize it +; +; Don't clobber the Daylight Savings Time bit in register B, because we +; stash the LastKnownGood "environment variable" there. +; + mov ax, 0bh + CMOS_READ + and al, 1 + mov ah, al + or ah, REGISTER_B_ENABLE_PERIODIC_INTERRUPT + mov al, 0bh + CMOS_WRITE ; Initialize it + mov al,0CH ; Register C + CMOS_READ ; Read to initialize + mov al,0DH ; Register D + CMOS_READ ; Read to initialize + mov dword ptr [KiseInterruptCount], 0 + + stdCall _HalpReleaseCmosSpinLock + +; +; Now enable the interrupt and start the counter +; (As a matter of fact, only IRQ8 can come through.) +; + xor eax, eax ; (eax) = 0, initialize loopcount +ALIGN 16 + sti + jmp kise10 + +ALIGN 16 +kise10: + sub eax, 1 ; increment the loopcount + jnz short kise10 + +if DBG +; +; Counter overflowed +; + + stdCall _DbgBreakPoint +endif + jmp short kise10 + +; +; Our RealTimeClock interrupt handler. The control comes here through +; irq 8. +; Note: we discard first real time clock interrupt and compute the +; permicrosecond loopcount on receiving of the second real time +; interrupt. This is because the first interrupt is generated +; based on the previous real time tick interval. +; + +RealTimeClockHandler: + + inc dword ptr KiseInterruptCount ; increment interrupt count + cmp dword ptr KiseInterruptCount,1 ; Is this the first interrupt? + jnz short kise25 ; no, its the second go process it + pop eax ; get rid of original ret addr + push offset FLAT:kise10 ; set new return addr + + + stdCall _HalpAcquireCmosSpinLock ; intr disabled + + mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A + CMOS_WRITE ; Initialize it +; +; Don't clobber the Daylight Savings Time bit in register B, because we +; stash the LastKnownGood "environment variable" there. +; + mov ax, 0bh + CMOS_READ + and al, 1 + mov ah, al + or ah, REGISTER_B_ENABLE_PERIODIC_INTERRUPT + mov al, 0bh + CMOS_WRITE ; Initialize it + mov al,0CH ; Register C + CMOS_READ ; Read to initialize + mov al,0DH ; Register D + CMOS_READ ; Read to initialize + + stdCall _HalpReleaseCmosSpinLock +; +; Dismiss the interrupt. +; + mov al, OCW2_NON_SPECIFIC_EOI ; send non specific eoi to slave + out PIC2_PORT0, al + mov al, PIC2_EOI ; specific eoi to master for pic2 eoi + out PIC1_PORT0, al ; send irq2 specific eoi to master + + xor eax, eax ; reset loop counter + + iretd + +kise25: + +; +; ** temporary - check for incorrect KeStallExecutionProcessorLoopCount +; + +if DBG + cmp eax, 0 + jnz short kise30 + stdCall _DbgBreakPoint + +endif + ; never return +; +; ** End temporay code +; + +kise30: + neg eax + xor edx, edx ; (edx:eax) = divident + mov ecx, PeriodInMicroSecond; (ecx) = time spent in the loop + div ecx ; (eax) = loop count per microsecond + cmp edx, 0 ; Is remainder =0? + jz short kise40 ; yes, go kise40 + inc eax ; increment loopcount by 1 +kise40: + mov PCR[PcStallScaleFactor], eax + mov HalpP0BugBugStallCount, eax + +; +; Reset return address to kexit +; + + pop eax ; discard original return address + push offset FLAT:kexit ; return to kexit + mov eax, (HIGHEST_LEVEL_FOR_8259 - RTCIRQ) + +; +; Shutdown periodic interrupt +; + stdCall _HalpAcquireCmosSpinLock + mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A + CMOS_WRITE ; Initialize it + mov ax, 0bh + CMOS_READ + and al, 1 + mov ah, al + or ah, REGISTER_B_DISABLE_PERIODIC_INTERRUPT + mov al, 0bh + CMOS_WRITE ; Initialize it + mov al,0CH ; Register C + CMOS_READ ; dismiss pending interrupt + stdCall _HalpReleaseCmosSpinLock + +; +; Dismiss the interrupt. +; + mov eax, RTCIRQ + mov al, OCW2_NON_SPECIFIC_EOI ; send non specific eoi to slave + out PIC2_PORT0, al + mov al, PIC2_EOI ; specific eoi to master for pic2 eoi + out PIC1_PORT0, al ; send irq2 specific eoi to master + + and word ptr [esp+8], NOT 0200H ; Disable interrupt upon return + iretd + +kexit: ; Interrupts are disabled + pop ecx ; (ecx) -> &IDT[HalProfileVector] + pop [ecx+4] ; restore higher half of RTC desc + pop [ecx] ; restore lower half of RTC desc + + pop eax ; (eax) = origianl 8259 int masks + SET_8259_MASK + + popfd ; restore caller's eflags + mov esp, ebp + pop ebp ; restore ebp + stdRET _HalpInitializeStallExecution + +stdENDP _HalpInitializeStallExecution + +INIT ends + +_TEXT SEGMENT PARA PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "Stall Execution" +;++ +; +; VOID +; KeStallExecutionProcessor ( +; IN ULONG MicroSeconds +; ) +; +; Routine Description: +; +; This function stalls execution for the specified number of microseconds. +; KeStallExecutionProcessor +; +; Arguments: +; +; MicroSeconds - Supplies the number of microseconds that execution is to be +; stalled. +; +; Return Value: +; +; None. +; +;-- + +MicroSeconds equ [esp + 4] + + +cPublicProc _KeStallExecutionProcessor ,1 +cPublicFpo 1, 0 + + mov ecx, MicroSeconds ; (ecx) = Microseconds + jecxz short kese10 ; return if no loop needed + + mov eax, PCR[PcStallScaleFactor] ; get per microsecond + ; loop count for the processor + mul ecx ; (eax) = desired loop count + +if DBG +; +; Make sure we the loopcount is less than 4G and is not equal to zero +; + + cmp edx, 0 + jz short @f + int 3 + +@@: cmp eax,0 + jnz short @f + int 3 +@@: +endif +ALIGN 16 + jmp kese05 + +ALIGN 16 +kese05: sub eax, 1 ; (eax) = (eax) - 1 + jnz short kese05 +kese10: + stdRET _KeStallExecutionProcessor + +stdENDP _KeStallExecutionProcessor + +_TEXT ends + + end diff --git a/private/ntos/nthals/halx86/i386/ixswint.asm b/private/ntos/nthals/halx86/i386/ixswint.asm new file mode 100644 index 000000000..2ca5c88e2 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixswint.asm @@ -0,0 +1,327 @@ + title "Software Interrupts" + +;++ +; +; Copyright (c) 1992 Microsoft Corporation +; +; Module Name: +; +; ixswint.asm +; +; Abstract: +; +; This module implements the software interrupt handlers +; for x86 machines +; +; Author: +; +; John Vert (jvert) 2-Jan-1992 +; +; Environment: +; +; Kernel mode only. +; +; Revision History: +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\ix8259.inc +include i386\kimacro.inc + .list + + EXTRNP _KiDeliverApc,3,IMPORT + EXTRNP _KiDispatchInterrupt,0,IMPORT + EXTRNP Kei386EoiHelper,0,IMPORT + EXTRNP _HalEndSystemInterrupt,2 + extrn SWInterruptHandlerTable:dword + extrn SWInterruptLookUpTable:byte +ifdef IRQL_METRICS + extrn HalApcSoftwareIntCount:dword + extrn HalDpcSoftwareIntCount:dword +endif + +_TEXT$02 SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "Request Software Interrupt" + +;++ +; +; VOID +; HalRequestSoftwareInterrupt ( +; IN KIRQL RequestIrql +; ) +; +; Routine Description: +; +; This routine is used to request a software interrupt to the +; system. Also, this routine checks to see if any software +; interrupt should be generated. +; The following condition will cause software interrupt to +; be simulated: +; any software interrupt which has higher priority than +; current IRQL's is pending. +; +; NOTE: This routine simulates software interrupt as long as +; any pending SW interrupt level is higher than the current +; IRQL, even when interrupts are disabled. +; +; Arguments: +; +; (cl) = RequestIrql - Supplies the request IRQL value +; +; Return Value: +; +; None. +; +;-- + +cPublicFastCall HalRequestSoftwareInterrupt ,1 +cPublicFpo 0, 1 + + mov eax,1 + shl eax, cl ; convert to mask + pushfd ; save interrupt mode + cli ; disable interrupt + or PCR[PcIRR], eax ; set the request bit + mov cl, PCR[PcIrql] ; get current IRQL + + mov eax, PCR[PcIRR] ; get SW interrupt request register + and eax, 3 ; mask off pending HW interrupts + + xor edx, edx + mov dl, SWInterruptLookUpTable[eax] ; get the highest pending + ; software interrupt level + cmp dl, cl ; Is highest SW int level > irql? + jbe short KsiExit ; No, jmp ksiexit + call SWInterruptHandlerTable[edx*4] ; yes, simulate interrupt + ; to the appropriate handler +KsiExit: + popfd ; restore original interrupt mode + fstRET HalRequestSoftwareInterrupt + +fstENDP HalRequestSoftwareInterrupt + + page ,132 + subttl "Request Software Interrupt" + +;++ +; +; VOID +; HalClearSoftwareInterrupt ( +; IN KIRQL RequestIrql +; ) +; +; Routine Description: +; +; This routine is used to clear a possible pending software interrupt. +; Support for this function is optional, and allows the kernel to +; reduce the number of spurious software interrupts it receives/ +; +; Arguments: +; +; (cl) = RequestIrql - Supplies the request IRQL value +; +; Return Value: +; +; None. +; +;-- + +cPublicFastCall HalClearSoftwareInterrupt ,1 +cPublicFpo 0, 0 + + mov eax,1 + shl eax, cl ; convert to mask + + not eax + and PCR[PcIRR], eax ; clear pending irr bit + + fstRET HalClearSoftwareInterrupt + +fstENDP HalClearSoftwareInterrupt + + + + page ,132 + subttl "Dispatch Interrupt" +;++ +; +; VOID +; HalpDispatchInterrupt( +; VOID +; ); +; +; Routine Description: +; +; This routine is the interrupt handler for a software interrupt generated +; at DISPATCH_LEVEL. Its function is to save the machine state, raise +; Irql to DISPATCH_LEVEL, dismiss the interrupt, and call the DPC +; delivery routine. +; +; Arguments: +; +; None +; Interrupt is disabled +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST hdpi_a, hdpi_t + + align dword + public _HalpDispatchInterrupt +_HalpDispatchInterrupt proc +ifdef IRQL_METRICS + lock inc HalDpcSoftwareIntCount +endif +; +; Create IRET frame on stack +; + pop eax + pushfd + push cs + push eax + +; +; Save machine state on trap frame +; + + ENTER_INTERRUPT hdpi_a, hdpi_t +.FPO ( FPO_LOCALS+1, 0, 0, 0, 0, FPO_TRAPFRAME ) + + public _HalpDispatchInterrupt2ndEntry +_HalpDispatchInterrupt2ndEntry: + +; Save previous IRQL and set new priority level + + push PCR[PcIrql] ; save previous IRQL + mov byte ptr PCR[PcIrql], DISPATCH_LEVEL; set new irql + and dword ptr PCR[PcIRR], not (1 shl DISPATCH_LEVEL) ; clear the pending bit in IRR + +; +; Now it is safe to enable interrupt to allow higher priority interrupt +; to come in. +; + + sti + +; +; Go do Dispatch Interrupt processing +; + stdCall _KiDispatchInterrupt + +; +; Do interrupt exit processing +; + + SOFT_INTERRUPT_EXIT ; will do an iret + +_HalpDispatchInterrupt endp + + page ,132 + subttl "APC Interrupt" +;++ +; +; HalpApcInterrupt( +; VOID +; ); +; +; Routine Description: +; +; This routine is entered as the result of a software interrupt generated +; at APC_LEVEL. Its function is to save the machine state, raise Irql to +; APC_LEVEL, dismiss the interrupt, and call the APC delivery routine. +; +; Arguments: +; +; None +; Interrupt is Disabled +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST hapc_a, hapc_t + + align dword + public _HalpApcInterrupt +_HalpApcInterrupt proc +ifdef IRQL_METRICS + lock inc HalApcSoftwareIntCount +endif +; +; Create IRET frame on stack +; + pop eax + pushfd + push cs + push eax + +; +; Save machine state in trap frame +; + ENTER_INTERRUPT hapc_a, hapc_t +.FPO ( FPO_LOCALS+1, 0, 0, 0, 0, FPO_TRAPFRAME ) + + + public _HalpApcInterrupt2ndEntry +_HalpApcInterrupt2ndEntry: + +; +; Save previous IRQL and set new priority level +; + + push PCR[PcIrql] ; save previous Irql + mov byte ptr PCR[PcIrql], APC_LEVEL ; set new Irql + and dword ptr PCR[PcIRR], not (1 shl APC_LEVEL) ; dismiss pending APC +; +; Now it is safe to enable interrupt to allow higher priority interrupt +; to come in. +; + + sti + +; +; call the APC delivery routine. +; + + mov eax, [ebp]+TsSegCs ; get interrupted code's CS + and eax, MODE_MASK ; extract the mode + + test dword ptr [ebp]+TsEFlags, EFLAGS_V86_MASK + jz short @f + + or eax, MODE_MASK ; If v86 frame, then set user_mode +@@: + +; +; call APC deliver routine +; Previous mode +; Null exception frame +; Trap frame + + stdCall _KiDeliverApc, + +; +; +; Do interrupt exit processing +; + + SOFT_INTERRUPT_EXIT ; will do an iret + +_HalpApcInterrupt endp + +_TEXT$02 ends + + end diff --git a/private/ntos/nthals/halx86/i386/ixsysbus.c b/private/ntos/nthals/halx86/i386/ixsysbus.c new file mode 100644 index 000000000..a5463cb31 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixsysbus.c @@ -0,0 +1,188 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixsysbus.c + +Abstract: + +Author: + +Environment: + +Revision History: + + +--*/ + +#include "halp.h" + +ULONG HalpDefaultInterruptAffinity; + +BOOLEAN +HalpTranslateSystemBusAddress( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ); + +ULONG +HalpGetSystemInterruptVector( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,HalpGetSystemInterruptVector) +#endif + + +BOOLEAN +HalpTranslateSystemBusAddress( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ) + +/*++ + +Routine Description: + + This function translates a bus-relative address space and address into + a system physical address. + +Arguments: + + BusAddress - Supplies the bus-relative address + + AddressSpace - Supplies the address space number. + Returns the host address space number. + + AddressSpace == 0 => memory space + AddressSpace == 1 => I/O space + + TranslatedAddress - Supplies a pointer to return the translated address + +Return Value: + + A return value of TRUE indicates that a system physical address + corresponding to the supplied bus relative address and bus address + number has been returned in TranslatedAddress. + + A return value of FALSE occurs if the translation for the address was + not possible + +--*/ + +{ + PSUPPORTED_RANGE pRange; + + pRange = NULL; + switch (*AddressSpace) { + case 0: + // verify memory address is within buses memory limits + for (pRange = &BusHandler->BusAddresses->PrefetchMemory; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + + if (!pRange) { + for (pRange = &BusHandler->BusAddresses->Memory; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + } + + break; + + case 1: + // verify IO address is within buses IO limits + for (pRange = &BusHandler->BusAddresses->IO; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + break; + } + + if (pRange) { + TranslatedAddress->QuadPart = BusAddress.QuadPart + pRange->SystemBase; + *AddressSpace = pRange->SystemAddressSpace; + return TRUE; + } + + return FALSE; +} + + +ULONG +HalpGetSystemInterruptVector( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ) + +/*++ + +Routine Description: + +Arguments: + + BusInterruptLevel - Supplies the bus specific interrupt level. + + BusInterruptVector - Supplies the bus specific interrupt vector. + + Irql - Returns the system request priority. + + Affinity - Returns the system wide irq affinity. + +Return Value: + + Returns the system interrupt vector corresponding to the specified device. + +--*/ +{ + ULONG SystemVector; + + UNREFERENCED_PARAMETER( BusHandler ); + UNREFERENCED_PARAMETER( RootHandler ); + UNREFERENCED_PARAMETER( BusInterruptVector ); + + SystemVector = BusInterruptLevel + PRIMARY_VECTOR_BASE; + if (SystemVector < PRIMARY_VECTOR_BASE || + SystemVector > PRIMARY_VECTOR_BASE + HIGHEST_LEVEL_FOR_8259 || + HalpIDTUsage[SystemVector].Flags & IDTOwned ) { + + // + // This is an illegal BusInterruptVector and cannot be connected. + // + + return(0); + } + + *Irql = (KIRQL)(HIGHEST_LEVEL_FOR_8259 + PRIMARY_VECTOR_BASE - SystemVector); + *Affinity = HalpDefaultInterruptAffinity; + ASSERT(HalpDefaultInterruptAffinity); + return SystemVector; +} + diff --git a/private/ntos/nthals/halx86/i386/ixsysint.asm b/private/ntos/nthals/halx86/i386/ixsysint.asm new file mode 100644 index 000000000..e73b40f24 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixsysint.asm @@ -0,0 +1,582 @@ +;++ +; +;Copyright (c) 1991 Microsoft Corporation +; +;Module Name: +; +; ixsysint.asm +; +;Abstract: +; +; This module implements the HAL routines to enable/disable system +; interrupts. +; +;Author: +; +; John Vert (jvert) 22-Jul-1991 +; +;Environment: +; +; Kernel Mode +; +;Revision History: +; +;-- + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\ix8259.inc +include i386\kimacro.inc +include mac386.inc + .list + + extrn KiI8259MaskTable:DWORD + EXTRNP _KeBugCheck,1,IMPORT + +ifdef IRQL_METRICS + extrn HalPostponedIntCount:dword +endif + extrn _PciIsaIrq:dword + extrn SWInterruptHandlerTable:dword + extrn HalpHardwareInterruptLevel:proc + +; +; Constants used to initialize CMOS/Real Time Clock +; + +CMOS_CONTROL_PORT EQU 70h ; command port for cmos +CMOS_DATA_PORT EQU 71h ; cmos data port + +; +; Macros to Read/Write/Reset CMOS to initialize RTC +; + +; CMOS_READ +; +; Description: This macro read a byte from the CMOS register specified +; in (AL). +; +; Parameter: (AL) = address/register to read +; Return: (AL) = data +; + +CMOS_READ MACRO + OUT CMOS_CONTROL_PORT,al ; ADDRESS LOCATION AND DISABLE NMI + IODelay ; I/O DELAY + IN AL,CMOS_DATA_PORT ; READ IN REQUESTED CMOS DATA + IODelay ; I/O DELAY +ENDM + +_DATA SEGMENT DWORD PUBLIC 'DATA' + +align dword +; +; HalDismissSystemInterrupt does an indirect jump through this table so it +; can quickly execute specific code for different interrupts. +; + public HalpSpecialDismissTable +HalpSpecialDismissTable label dword + dd offset FLAT:HalpDismissNormal ; irq 0 + dd offset FLAT:HalpDismissNormal ; irq 1 + dd offset FLAT:HalpDismissNormal ; irq 2 + dd offset FLAT:HalpDismissNormal ; irq 3 + dd offset FLAT:HalpDismissNormal ; irq 4 + dd offset FLAT:HalpDismissNormal ; irq 5 + dd offset FLAT:HalpDismissNormal ; irq 6 + dd offset FLAT:HalpDismissIrq07 ; irq 7 + dd offset FLAT:HalpDismissNormal ; irq 8 + dd offset FLAT:HalpDismissNormal ; irq 9 + dd offset FLAT:HalpDismissNormal ; irq A + dd offset FLAT:HalpDismissNormal ; irq B + dd offset FLAT:HalpDismissNormal ; irq C + dd offset FLAT:HalpDismissIrq0d ; irq D + dd offset FLAT:HalpDismissNormal ; irq E + dd offset FLAT:HalpDismissIrq0f ; irq F + dd offset FLAT:HalpDismissNormal ; irq 10 + dd offset FLAT:HalpDismissNormal ; irq 11 + dd offset FLAT:HalpDismissNormal ; irq 12 + dd offset FLAT:HalpDismissNormal ; irq 13 + dd offset FLAT:HalpDismissNormal ; irq 14 + dd offset FLAT:HalpDismissNormal ; irq 15 + dd offset FLAT:HalpDismissNormal ; irq 16 + dd offset FLAT:HalpDismissNormal ; irq 17 + dd offset FLAT:HalpDismissNormal ; irq 18 + dd offset FLAT:HalpDismissNormal ; irq 19 + dd offset FLAT:HalpDismissNormal ; irq 1A + dd offset FLAT:HalpDismissNormal ; irq 1B + dd offset FLAT:HalpDismissNormal ; irq 1C + dd offset FLAT:HalpDismissNormal ; irq 1D + dd offset FLAT:HalpDismissNormal ; irq 1E + dd offset FLAT:HalpDismissNormal ; irq 1F + dd offset FLAT:HalpDismissNormal ; irq 20 + dd offset FLAT:HalpDismissNormal ; irq 21 + dd offset FLAT:HalpDismissNormal ; irq 22 + dd offset FLAT:HalpDismissNormal ; irq 23 + + public HalpSpecialDismissLevelTable +HalpSpecialDismissLevelTable label dword + dd offset FLAT:HalpDismissLevel ; irq 0 + dd offset FLAT:HalpDismissLevel ; irq 1 + dd offset FLAT:HalpDismissLevel ; irq 2 + dd offset FLAT:HalpDismissLevel ; irq 3 + dd offset FLAT:HalpDismissLevel ; irq 4 + dd offset FLAT:HalpDismissLevel ; irq 5 + dd offset FLAT:HalpDismissLevel ; irq 6 + dd offset FLAT:HalpDismissIrq07Level ; irq 7 + dd offset FLAT:HalpDismissLevel ; irq 8 + dd offset FLAT:HalpDismissLevel ; irq 9 + dd offset FLAT:HalpDismissLevel ; irq A + dd offset FLAT:HalpDismissLevel ; irq B + dd offset FLAT:HalpDismissLevel ; irq C + dd offset FLAT:HalpDismissIrq0dLevel ; irq D + dd offset FLAT:HalpDismissLevel ; irq E + dd offset FLAT:HalpDismissIrq0fLevel ; irq F + +_DATA ENDS + +_TEXT$01 SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + +;++ +;BOOLEAN +;HalBeginSystemInterrupt( +; IN KIRQL Irql +; IN CCHAR Vector, +; OUT PKIRQL OldIrql +; ) +; +; +; +;Routine Description: +; +; This routine is used to dismiss the specified vector number. It is called +; before any interrupt service routine code is executed. +; +; N.B. This routine does NOT preserve EAX or EBX +; +; On a UP machine the interrupt dismissed at BeginSystemInterrupt time. +; This is fine since the irql is being raise to mask it off. +; HalEndSystemInterrupt is simply a LowerIrql request. +; +; +;Arguments: +; +; Irql - Supplies the IRQL to raise to +; +; Vector - Supplies the vector of the interrupt to be dismissed +; +; OldIrql- Location to return OldIrql +; +; +;Return Value: +; +; FALSE - Interrupt is spurious and should be ignored +; +; TRUE - Interrupt successfully dismissed and Irql raised. +; +;-- +align dword +HbsiIrql equ byte ptr [esp+4] +HbsiVector equ byte ptr [esp+8] +HbsiOldIrql equ dword ptr [esp+12] + +cPublicProc _HalBeginSystemInterrupt ,3 +.FPO ( 0, 3, 0, 0, 0, 0 ) + xor ecx, ecx + mov cl, HbsiVector ; (ecx) = System Vector + sub ecx, PRIMARY_VECTOR_BASE ; (ecx) = 8259 IRQ # +if DBG + cmp ecx, 1fh + jbe hbsi00 + int 3 +hbsi00: + +endif + jmp HalpSpecialDismissTable[ecx*4] + +HalpDismissIrq0f: +; +; Check to see if this is a spurious interrupt +; + mov al, OCW3_READ_ISR ; tell 8259 we want to read ISR + out PIC2_PORT0, al + IODelay ; delay + in al, PIC2_PORT0 ; (al) = content of PIC 1 ISR + test al, 10000000B ; Is In-Service register set? + jnz short HalpDismissNormal ; No, this is NOT a spurious int, + ; go do the normal interrupt stuff +HalpIrq0fSpurious: +; +; This is a spurious interrupt. +; Because the slave PIC is cascaded to irq2 of master PIC, we need to +; dismiss the interupt on master PIC's irq2. +; + + mov al, PIC2_EOI ; Specific eoi to master for pic2 eoi + out PIC1_PORT0, al ; send irq2 specific eoi to master + mov eax,0 ; return FALSE + stdRET _HalBeginSystemInterrupt + +HalpDismissIrq07: +; +; Check to see if this is a spurious interrupt +; + mov al, OCW3_READ_ISR ; tell 8259 we want to read ISR + out PIC1_PORT0, al + IODelay ; delay + in al, PIC1_PORT0 ; (al) = content of PIC 1 ISR + test al, 10000000B ; Is In-Service register set? + jnz HalpDismissNormal ; No, so this is NOT a spurious int + mov eax, 0 ; return FALSE + stdRET _HalBeginSystemInterrupt + +HalpDismissIrq0d: +; +; Clear the NPX busy latch. +; + + xor al,al + out I386_80387_BUSY_PORT, al + +align 4 +HalpDismissNormal: +; +; Raise IRQL to requested level +; + xor ebx,ebx + mov al, HbsiIrql ; (al) = New irql + ; (ecx) = IRQ # + mov bl, PCR[PcIrql] ; (ebx) = Current Irql + +; +; Now we check to make sure the Irql of this interrupt > current Irql. +; If it is not, we dismiss it as spurious and set the appropriate bit +; in the IRR so we can dispatch the interrupt when Irql is lowered +; + cmp al, bl + jbe Hdsi300 + + mov PCR[PcIrql], al ; set new Irql + mov edx, HbsiOldIrql ; save current irql to OldIrql variable + mov byte ptr [edx], bl + +; +; Dismiss interrupt. +; + mov eax, ecx ; (eax) = IRQ # + cmp eax, 8 ; EOI to master or slave? + jae short Hbsi100 ; EIO to both master and slave + + or al, PIC1_EOI_MASK ; create specific eoi mask for master + out PIC1_PORT0, al ; dismiss the interrupt + sti + mov eax, 1 ; return TRUE + stdRET _HalBeginSystemInterrupt + +align 4 +Hbsi100: + add al, OCW2_SPECIFIC_EOI - 8 ; specific eoi to slave + out PIC2_PORT0, al + + mov al, PIC2_EOI ; specific eoi to master for pic2 eoi + out PIC1_PORT0, al ; send irq2 specific eoi to master + sti + mov eax, 1 ; return TRUE + stdRET _HalBeginSystemInterrupt + +align 4 +Hdsi300: +; +; An interrupt has come in at a lower Irql, so we dismiss it as spurious and +; set the appropriate bit in the IRR so that KeLowerIrql knows to dispatch +; it when Irql is lowered. +; +; (ecx) = 8259 IRQ# +; (al) = New Irql +; (ebx) = Current Irql +; + + mov eax, 1 + add ecx, 4 ; (ecx) = Irq # + 4 + shl eax, cl + or PCR[PcIRR], eax + +; +; Raise Irql to prevent it from happening again +; + +; +; Get the PIC masks for Irql +; + + mov eax, KiI8259MaskTable[ebx*4] + or eax, PCR[PcIDR] +; +; Write the new interrupt mask register back to the 8259 +; + SET_8259_MASK + +Hbsi390: + +ifdef IRQL_METRICS + lock inc HalPostponedIntCount +endif + + xor eax, eax ; return FALSE, spurious interrupt + stdRET _HalBeginSystemInterrupt + + +HalpDismissIrq0fLevel: +; +; Check to see if this is a spurious interrupt +; + mov al, OCW3_READ_ISR ; tell 8259 we want to read ISR + out PIC2_PORT0, al + IODelay ; delay + in al, PIC2_PORT0 ; (al) = content of PIC 1 ISR + test al, 10000000B ; Is In-Service register set? + jnz short HalpDismissLevel ; No, this is NOT a spurious int, + ; go do the normal interrupt stuff + jmp HalpIrq0fSpurious + +HalpDismissIrq07Level: +; +; Check to see if this is a spurious interrupt +; + mov al, OCW3_READ_ISR ; tell 8259 we want to read ISR + out PIC1_PORT0, al + IODelay ; delay + in al, PIC1_PORT0 ; (al) = content of PIC 1 ISR + test al, 10000000B ; Is In-Service register set? + jnz short HalpDismissLevel ; No, so this is NOT a spurious int + mov eax, 0 ; return FALSE + stdRET _HalBeginSystemInterrupt + +HalpDismissIrq0dLevel: +; +; Clear the NPX busy latch. +; + + xor al,al + out I386_80387_BUSY_PORT, al + +align 4 +HalpDismissLevel: +; +; Mask this level interrupt off +; (ecx) = 8259 IRQ# +; + mov al, HbsiIrql ; (al) = New irql + mov eax, KiI8259MaskTable[eax*4] ; get 8259's masks + or eax, PCR[PcIDR] ; mask disabled irqs + SET_8259_MASK ; send mask to 8259s +; +; The SWInterruptHandler for this vector has been set to a NOP. +; Set the vector's IRR so that Lower Irql will clear the 8259 mask for this +; Irq when the irql is lowered below this level. +; + mov eax, ecx ; (eax) = Irq # + mov ebx, 1 + add ecx, 4 ; (ecx) = Irq # + 4 + shl ebx, cl + or PCR[PcIRR], ebx + +; +; Dismiss interrupt. Current interrupt is already masked off. +; Then check to make sure the Irql of this interrupt > current Irql. +; If it is not, we dismiss it as spurious - since this is a level interrupt +; when the 8259's are unmasked the interrupt will reoccur +; + mov cl, HbsiIrql + mov bl, PCR[PcIrql] + mov edx, HbsiOldIrql + + cmp eax, 8 ; EOI to master or slave? + jae short Hbsi450 ; EIO to both master and slave + + or al, PIC1_EOI_MASK ; create specific eoi mask for master + out PIC1_PORT0, al ; dismiss the interrupt + + cmp cl, bl + jbe short Hbsi390 ; Spurious? + + mov PCR[PcIrql], cl ; raise to new irql + mov byte ptr [edx], bl ; return old irql + sti + mov eax, 1 ; return TRUE + stdRET _HalBeginSystemInterrupt + +align 4 +Hbsi450: + add al, OCW2_SPECIFIC_EOI - 8 ; specific eoi to slave + out PIC2_PORT0, al + mov al, PIC2_EOI ; specific eoi to master for pic2 eoi + out PIC1_PORT0, al ; send irq2 specific eoi to master + + cmp cl, bl + jbe Hbsi390 ; Spurious? + + mov PCR[PcIrql], cl ; raise to new irql + mov byte ptr [edx], bl ; return old irql + sti + mov eax, 1 ; return TRUE + stdRET _HalBeginSystemInterrupt + +stdENDP _HalBeginSystemInterrupt + + +;++ +;VOID +;HalDisableSystemInterrupt( +; IN CCHAR Vector, +; IN KIRQL Irql +; ) +; +; +; +;Routine Description: +; +; Disables a system interrupt. +; +;Arguments: +; +; Vector - Supplies the vector of the interrupt to be disabled +; +; Irql - Supplies the interrupt level of the interrupt to be disabled +; +;Return Value: +; +; None. +; +;-- +cPublicProc _HalDisableSystemInterrupt ,2 +.FPO ( 0, 2, 0, 0, 0, 0 ) + +; + + movzx ecx, byte ptr [esp+4] ; (ecx) = Vector + sub ecx, PRIMARY_VECTOR_BASE ; (ecx) = 8259 irq # + mov edx, 1 + shl edx, cl ; (ebx) = bit in IMR to disable + cli + or PCR[PcIDR], edx + xor eax, eax + +; +; Get the current interrupt mask register from the 8259 +; + in al, PIC2_PORT1 + shl eax, 8 + in al, PIC1_PORT1 +; +; Mask off the interrupt to be disabled +; + or eax, edx +; +; Write the new interrupt mask register back to the 8259 +; + out PIC1_PORT1, al + shr eax, 8 + out PIC2_PORT1, al + PIC2DELAY + + sti + stdRET _HalDisableSystemInterrupt + +stdENDP _HalDisableSystemInterrupt + +;++ +; +;BOOLEAN +;HalEnableSystemInterrupt( +; IN ULONG Vector, +; IN KIRQL Irql, +; IN KINTERRUPT_MODE InterruptMode +; ) +; +; +;Routine Description: +; +; Enables a system interrupt +; +;Arguments: +; +; Vector - Supplies the vector of the interrupt to be enabled +; +; Irql - Supplies the interrupt level of the interrupt to be enabled. +; +;Return Value: +; +; None. +; +;-- +cPublicProc _HalEnableSystemInterrupt ,3 +.FPO ( 0, 3, 0, 0, 0, 0 ) + + movzx ecx, byte ptr [esp+4] ; (ecx) = vector + sub ecx, PRIMARY_VECTOR_BASE + jc hes_error + cmp ecx, CLOCK2_LEVEL + jnc hes_error + +; +; If this interrupt is mapped through ISA via the PCI bus then it's +; a level interrupt - treat it as such. +; + bt _PciIsaIrq, ecx + jnc short @f + +; +; It's OK to treat a level interrupt as an edge interrupt (just a performance +; slow-down), but it's not OK to treat an edge interrupt as a level interrupt. +; if the driver didn't say it's level, treat it as an edge +; + + mov al, [esp+12] + cmp al, 0 + jnz short @f + + mov SWInterruptHandlerTable+4*4[ecx*4], offset HalpHardwareInterruptLevel + + mov edx, HalpSpecialDismissLevelTable[ecx*4] + mov HalpSpecialDismissTable[ecx*4], edx + +@@: + mov eax, 1 + shl eax, cl ; (ebx) = bit in IMR to enable + not eax + + cli + and PCR[PcIDR], eax + +; +; Get the PIC masks for Irql 0 +; + mov eax, KiI8259MaskTable[0] + or eax, PCR[PcIDR] +; +; Write the new interrupt mask register back to the 8259 +; + SET_8259_MASK + + sti + mov eax, 1 ; return TRUE + stdRET _HalEnableSystemInterrupt + +hes_error: +if DBG + int 3 +endif + xor eax, eax ; FALSE + stdRET _HalEnableSystemInterrupt + +stdENDP _HalEnableSystemInterrupt + + +_TEXT$01 ENDS + + END diff --git a/private/ntos/nthals/halx86/i386/ixthunk.c b/private/ntos/nthals/halx86/i386/ixthunk.c new file mode 100644 index 000000000..d365f1933 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixthunk.c @@ -0,0 +1,82 @@ +/*++ + + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ixthunk.c + +Abstract: + + This module contains the standard call routines which thunk to + fastcall routines. + +Author: + + Ken Reneris (kenr) 04-May-1994 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "halp.h" + +#ifdef KeRaiseIrql +#undef KeRaiseIrql +#endif + +VOID +KeRaiseIrql ( + IN KIRQL NewIrql, + OUT PKIRQL OldIrql + ) +{ + *OldIrql = KfRaiseIrql (NewIrql); +} + + +#ifdef KeLowerIrql +#undef KeLowerIrql +#endif + + +VOID +KeLowerIrql ( + IN KIRQL NewIrql + ) +{ + KfLowerIrql (NewIrql); +} + +#ifdef KeAcquireSpinLock +#undef KeAcquireSpinLock +#endif + +VOID +KeAcquireSpinLock ( + IN PKSPIN_LOCK SpinLock, + OUT PKIRQL OldIrql + ) +{ + *OldIrql = KfAcquireSpinLock (SpinLock); +} + + +#ifdef KeReleaseSpinLock +#undef KeReleaseSpinLock +#endif + +VOID +KeReleaseSpinLock ( + IN PKSPIN_LOCK SpinLock, + IN KIRQL NewIrql + ) +{ + KfReleaseSpinLock (SpinLock, NewIrql); +} diff --git a/private/ntos/nthals/halx86/i386/ixusage.c b/private/ntos/nthals/halx86/i386/ixusage.c new file mode 100644 index 000000000..e0efb9b20 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixusage.c @@ -0,0 +1,535 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ixusage.c + +Abstract: + +Author: + + Ken Reneris (kenr) + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "halp.h" + + +// +// Array to remember hal's IDT usage +// + +extern ADDRESS_USAGE *HalpAddressUsageList; +extern IDTUsage HalpIDTUsage[MAXIMUM_IDTVECTOR]; +extern WCHAR HalpSzSystem[]; +extern WCHAR HalpSzSerialNumber[]; + +KAFFINITY HalpActiveProcessors; + +VOID +HalpGetResourceSortValue ( + IN PCM_PARTIAL_RESOURCE_DESCRIPTOR pRCurLoc, + OUT PULONG sortscale, + OUT PLARGE_INTEGER sortvalue + ); + +VOID +HalpReportSerialNumber ( + VOID + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,HalpEnableInterruptHandler) +#pragma alloc_text(INIT,HalpRegisterVector) +#pragma alloc_text(INIT,HalpGetResourceSortValue) +#pragma alloc_text(INIT,HalpReportResourceUsage) +#pragma alloc_text(INIT,HalpReportSerialNumber) +#endif + + + + +VOID +HalpEnableInterruptHandler ( + IN UCHAR ReportFlags, + IN ULONG BusInterruptVector, + IN ULONG SystemInterruptVector, + IN KIRQL SystemIrql, + IN VOID (*HalInterruptServiceRoutine)(VOID), + IN KINTERRUPT_MODE InterruptMode + ) +/*++ + +Routine Description: + + This function connects & registers an IDT vectors usage by the HAL. + +Arguments: + +Return Value: + +--*/ +{ + // + // Remember which vector the hal is connecting so it can be reported + // later on + // + HalpRegisterVector (ReportFlags, BusInterruptVector, SystemInterruptVector, SystemIrql); + + + // + // Connect the IDT and enable the vector now + // + + KiSetHandlerAddressToIDT(SystemInterruptVector, HalInterruptServiceRoutine); + HalEnableSystemInterrupt(SystemInterruptVector, SystemIrql, InterruptMode); +} + + + +VOID +HalpRegisterVector ( + IN UCHAR ReportFlags, + IN ULONG BusInterruptVector, + IN ULONG SystemInterruptVector, + IN KIRQL SystemIrql + ) +/*++ + +Routine Description: + + This registers an IDT vectors usage by the HAL. + +Arguments: + +Return Value: + +--*/ +{ +#if DBG + // There are only 0ff IDT entries... + ASSERT (SystemInterruptVector <= MAXIMUM_IDTVECTOR && + BusInterruptVector <= MAXIMUM_IDTVECTOR); +#endif + + // + // Remember which vector the hal is connecting so it can be reported + // later on + // + + HalpIDTUsage[SystemInterruptVector].Flags = ReportFlags; + HalpIDTUsage[SystemInterruptVector].Irql = SystemIrql; + HalpIDTUsage[SystemInterruptVector].BusReleativeVector = (UCHAR) BusInterruptVector; +} + + +VOID +HalpGetResourceSortValue ( + IN PCM_PARTIAL_RESOURCE_DESCRIPTOR pRCurLoc, + OUT PULONG sortscale, + OUT PLARGE_INTEGER sortvalue + ) +/*++ + +Routine Description: + + Used by HalpReportResourceUsage in order to properly sort + partial_resource_descriptors. + +Arguments: + + pRCurLoc - resource descriptor + +Return Value: + + sortscale - scaling of resource descriptor for sorting + sortvalue - value to sort on + + +--*/ +{ + switch (pRCurLoc->Type) { + case CmResourceTypeInterrupt: + *sortscale = 0; + *sortvalue = RtlConvertUlongToLargeInteger( + pRCurLoc->u.Interrupt.Level ); + break; + + case CmResourceTypePort: + *sortscale = 1; + *sortvalue = pRCurLoc->u.Port.Start; + break; + + case CmResourceTypeMemory: + *sortscale = 2; + *sortvalue = pRCurLoc->u.Memory.Start; + break; + + default: + *sortscale = 4; + *sortvalue = RtlConvertUlongToLargeInteger (0); + break; + } +} + + +VOID +HalpReportResourceUsage ( + IN PUNICODE_STRING HalName, + IN INTERFACE_TYPE DeviceInterfaceToUse + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + PCM_RESOURCE_LIST RawResourceList, TranslatedResourceList; + PCM_FULL_RESOURCE_DESCRIPTOR pRFullDesc, pTFullDesc; + PCM_PARTIAL_RESOURCE_LIST pRPartList, pTPartList; + PCM_PARTIAL_RESOURCE_DESCRIPTOR pRCurLoc, pTCurLoc; + PCM_PARTIAL_RESOURCE_DESCRIPTOR pRSortLoc, pTSortLoc; + CM_PARTIAL_RESOURCE_DESCRIPTOR RPartialDesc, TPartialDesc; + ULONG i, j, k, ListSize, Count; + ULONG curscale, sortscale; + UCHAR pass, reporton; + INTERFACE_TYPE interfacetype; + ULONG CurrentIDT, CurrentElement; + ADDRESS_USAGE *CurrentAddress; + LARGE_INTEGER curvalue, sortvalue; + + + // + // Allocate some space to build the resource structure + // + + RawResourceList = (PCM_RESOURCE_LIST) ExAllocatePool (NonPagedPool, PAGE_SIZE*2); + TranslatedResourceList = (PCM_RESOURCE_LIST) ExAllocatePool (NonPagedPool, PAGE_SIZE*2); + + // This functions assumes unset fields are zero + RtlZeroMemory (RawResourceList, PAGE_SIZE*2); + RtlZeroMemory (TranslatedResourceList, PAGE_SIZE*2); + + // + // Initialize the lists + // + + RawResourceList->List[0].InterfaceType = (INTERFACE_TYPE) -1; + + pRFullDesc = RawResourceList->List; + pRCurLoc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) RawResourceList->List; + pTCurLoc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) TranslatedResourceList->List; + + // + // Make sure all vectors 00-2f are reserved + // 00-1E reserved by Intel + // 1F reserved by Intel for APIC (apc priority level) + // 20-2e reserved by Microsoft + // 2f reserved by Microsoft for APIC (dpc priority level) + // + + for(i=0; i < PRIMARY_VECTOR_BASE; i++) { + if (!(HalpIDTUsage[i].Flags & IDTOwned)) { + HalpIDTUsage[i].Flags = InternalUsage; + HalpIDTUsage[i].BusReleativeVector = (UCHAR) i; + } + } + + for(pass=0; pass < 2; pass++) { + if (pass == 0) { + // + // First pass - build resource lists for resources reported + // reported against device usage. + // + + reporton = DeviceUsage & ~IDTOwned; + interfacetype = DeviceInterfaceToUse; + } else { + + // + // Second pass = build reousce lists for resources reported + // as internal usage. + // + + reporton = InternalUsage & ~IDTOwned; + interfacetype = Internal; + } + + CurrentIDT = 0; + CurrentElement = 0; + CurrentAddress = HalpAddressUsageList; + + for (; ;) { + if (CurrentIDT <= MAXIMUM_IDTVECTOR) { + // + // Check to see if CurrentIDT needs to be reported + // + + if (!(HalpIDTUsage[CurrentIDT].Flags & reporton)) { + // Don't report on this one + CurrentIDT++; + continue; + } + + // + // Report CurrentIDT resource + // + + RPartialDesc.Type = CmResourceTypeInterrupt; + RPartialDesc.ShareDisposition = CmResourceShareDriverExclusive; + RPartialDesc.Flags = + HalpIDTUsage[CurrentIDT].Flags & InterruptLatched ? + CM_RESOURCE_INTERRUPT_LATCHED : + CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; + RPartialDesc.u.Interrupt.Vector = HalpIDTUsage[CurrentIDT].BusReleativeVector; + RPartialDesc.u.Interrupt.Level = HalpIDTUsage[CurrentIDT].BusReleativeVector; + RPartialDesc.u.Interrupt.Affinity = HalpActiveProcessors; + + RtlCopyMemory (&TPartialDesc, &RPartialDesc, sizeof TPartialDesc); + TPartialDesc.u.Interrupt.Vector = CurrentIDT; + TPartialDesc.u.Interrupt.Level = HalpIDTUsage[CurrentIDT].Irql; + + CurrentIDT++; + + } else { + // + // Check to see if CurrentAddress needs to be reported + // + + if (!CurrentAddress) { + break; // No addresses left + } + + if (!(CurrentAddress->Flags & reporton)) { + // Don't report on this list + CurrentElement = 0; + CurrentAddress = CurrentAddress->Next; + continue; + } + + if (!CurrentAddress->Element[CurrentElement].Length) { + // End of current list, go to next list + CurrentElement = 0; + CurrentAddress = CurrentAddress->Next; + continue; + } + + // + // Report CurrentAddress + // + + RPartialDesc.Type = (UCHAR) CurrentAddress->Type; + RPartialDesc.ShareDisposition = CmResourceShareDriverExclusive; + + if (RPartialDesc.Type == CmResourceTypePort) { + i = 1; // address space port + RPartialDesc.Flags = CM_RESOURCE_PORT_IO; + } else { + i = 0; // address space memory + RPartialDesc.Flags = CM_RESOURCE_MEMORY_READ_WRITE; + } + + // Notice: assuming u.Memory and u.Port have the same layout + RPartialDesc.u.Memory.Start.HighPart = 0; + RPartialDesc.u.Memory.Start.LowPart = + CurrentAddress->Element[CurrentElement].Start; + + RPartialDesc.u.Memory.Length = + CurrentAddress->Element[CurrentElement].Length; + + // translated address = Raw address + RtlCopyMemory (&TPartialDesc, &RPartialDesc, sizeof TPartialDesc); + HalTranslateBusAddress ( + interfacetype, // device bus or internal + 0, // bus number + RPartialDesc.u.Memory.Start, // source address + &i, // address space + &TPartialDesc.u.Memory.Start ); // translated address + + if (RPartialDesc.Type == CmResourceTypePort && i == 0) { + TPartialDesc.Flags = CM_RESOURCE_PORT_MEMORY; + } + + CurrentElement++; + } + + // + // Include the current resource in the HALs list + // + + if (pRFullDesc->InterfaceType != interfacetype) { + // + // Interface type changed, add another full section + // + + RawResourceList->Count++; + TranslatedResourceList->Count++; + + pRFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR) pRCurLoc; + pTFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR) pTCurLoc; + + pRFullDesc->InterfaceType = interfacetype; + pTFullDesc->InterfaceType = interfacetype; + + pRPartList = &pRFullDesc->PartialResourceList; + pTPartList = &pTFullDesc->PartialResourceList; + + // + // Bump current location pointers up + // + pRCurLoc = pRFullDesc->PartialResourceList.PartialDescriptors; + pTCurLoc = pTFullDesc->PartialResourceList.PartialDescriptors; + } + + + pRPartList->Count++; + pTPartList->Count++; + RtlCopyMemory (pRCurLoc, &RPartialDesc, sizeof RPartialDesc); + RtlCopyMemory (pTCurLoc, &TPartialDesc, sizeof TPartialDesc); + + pRCurLoc++; + pTCurLoc++; + } + } + + ListSize = (ULONG) ( ((PUCHAR) pRCurLoc) - ((PUCHAR) RawResourceList) ); + + // + // The HAL's resource usage structures have been built + // Sort the partial lists based on the Raw resource values + // + + pRFullDesc = RawResourceList->List; + pTFullDesc = TranslatedResourceList->List; + + for (i=0; i < RawResourceList->Count; i++) { + + pRCurLoc = pRFullDesc->PartialResourceList.PartialDescriptors; + pTCurLoc = pTFullDesc->PartialResourceList.PartialDescriptors; + Count = pRFullDesc->PartialResourceList.Count; + + for (j=0; j < Count; j++) { + HalpGetResourceSortValue (pRCurLoc, &curscale, &curvalue); + + pRSortLoc = pRCurLoc; + pTSortLoc = pTCurLoc; + + for (k=j; k < Count; k++) { + HalpGetResourceSortValue (pRSortLoc, &sortscale, &sortvalue); + + if (sortscale < curscale || + (sortscale == curscale && + RtlLargeIntegerLessThan (sortvalue, curvalue)) ) { + + // + // Swap the elements.. + // + + RtlCopyMemory (&RPartialDesc, pRCurLoc, sizeof RPartialDesc); + RtlCopyMemory (pRCurLoc, pRSortLoc, sizeof RPartialDesc); + RtlCopyMemory (pRSortLoc, &RPartialDesc, sizeof RPartialDesc); + + // swap translated descriptor as well + RtlCopyMemory (&TPartialDesc, pTCurLoc, sizeof TPartialDesc); + RtlCopyMemory (pTCurLoc, pTSortLoc, sizeof TPartialDesc); + RtlCopyMemory (pTSortLoc, &TPartialDesc, sizeof TPartialDesc); + + // get new curscale & curvalue + HalpGetResourceSortValue (pRCurLoc, &curscale, &curvalue); + } + + pRSortLoc++; + pTSortLoc++; + } + + pRCurLoc++; + pTCurLoc++; + } + + pRFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR) pRCurLoc; + pTFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR) pTCurLoc; + } + + + // + // Inform the IO system of our resources.. + // + + IoReportHalResourceUsage ( + HalName, + RawResourceList, + TranslatedResourceList, + ListSize + ); + + ExFreePool (RawResourceList); + ExFreePool (TranslatedResourceList); + + // + // Add system's serial number + // + + HalpReportSerialNumber (); +} + +VOID +HalpReportSerialNumber ( + VOID + ) +{ + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING unicodeString; + HANDLE hSystem; + NTSTATUS status; + + if (!HalpSerialLen) { + return ; + } + + // + // Open HKEY_LOCAL_MACHINE\Hardware\Description\System + // + + RtlInitUnicodeString (&unicodeString, HalpSzSystem); + InitializeObjectAttributes ( + &objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + NULL, // handle + NULL + ); + + + status = ZwOpenKey (&hSystem, KEY_READ | KEY_WRITE, &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Add "Serial Number" as REG_BINARY + // + + RtlInitUnicodeString (&unicodeString, HalpSzSerialNumber); + + ZwSetValueKey ( + hSystem, + &unicodeString, + 0L, + REG_BINARY, + HalpSerialNumber, + HalpSerialLen + ); + + ZwClose (hSystem); + } +} diff --git a/private/ntos/nthals/halx86/i386/ncrdetct.c b/private/ntos/nthals/halx86/i386/ncrdetct.c new file mode 100644 index 000000000..2baf83ffa --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ncrdetct.c @@ -0,0 +1,253 @@ +/*++ + +Copyright (c) 1992 NCR Corporation + +Module Name: + + ncrdetect.c + +Abstract: + +Authors: + + Richard Barton (o-richb) 24-Jan-1992 + Brian Weischedel 30-Nov-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#ifndef _NTOS_ +#include "nthal.h" +#endif + +PVOID +HalpMapPhysicalMemory( + IN PVOID PhysicalAddress, + IN ULONG NumberPages + ); + +VOID +ReadCMOS( + IN ULONG StartingOffset, + IN ULONG Count, + IN PUCHAR ReturnValuePtr); + +ULONG NCRPlatform; + +#define NCR3450 0x35333433 // Copied here to build standalone +#define NCR3550 0x30353834 +#define NCR3360 0x33333630 + +// WPD definitions: + +PUCHAR WPDStringID = "NCR Voyager-1"; +PUCHAR WPDPlatformName = "System 3360"; +#define WPDStringIDLength 13 +#define WPDStringIDRangeStart (0xE000 << 4) // physical address +#define WPDStringIDRangeSize 0x10000 // 1 segment (64k) + +// MSBU definitions: + +PUCHAR MSBUCopyrightString = "Copyright (C) ???? NCR\0"; +#define MSBUCopyrightStringLen 23 +#define MSBUCopyrightPhysicalPtr ((0xF000 << 4) + (0xE020)) +typedef struct { + ULONG ClassFromFirmware; + PUCHAR PlatformName; +} MSBUPlatformMapEntry; +MSBUPlatformMapEntry MSBUPlatformMap[] = {{NCR3450, "System 3450"}, + {NCR3550, "System 3550"}, + {0, 0}}; + +PUCHAR +NCRDeterminePlatform( + OUT PBOOLEAN IsConfiguredMp +) +/*++ + +Routine Description: + Determine on which NCR platform we are running. For now just display + a message. Later we may not continue the boot if we're on an + unrecognized platform. + +Arguments: + none. + +Return Value: + Pointer to character string identifying which NCR platform. NULL means + it is unrecognized, and we shouldn't continue. + +--*/ +{ + BOOLEAN Matchfound; + MSBUPlatformMapEntry *MSBUPlatformMapPtr; + PVOID BIOSPagePtr; + PUCHAR StringPtr; + PUCHAR CopyrightPtr; + PUCHAR SearchPtr; + UCHAR CpuFlags; + + + // first check for a WPD platform by searching the 0xE000 BIOS segment + // for a ROM string that identifies this system as a 3360 + + + // get virtual address to the BIOS region (assuming region is both + // page aligned and multiple pages in size) + + BIOSPagePtr = HalpMapPhysicalMemory((PVOID) WPDStringIDRangeStart, + (WPDStringIDRangeSize >> 12)); + + if (BIOSPagePtr != NULL) { + + SearchPtr = BIOSPagePtr; // begin search at start of region + Matchfound = FALSE; + + // search until string is found or we are beyond the region + + while (!Matchfound && (SearchPtr <= (PUCHAR)((ULONG)BIOSPagePtr + + WPDStringIDRangeSize - + WPDStringIDLength))) { + + // see if SearchPtr points to the desired string + + StringPtr = (PUCHAR)((ULONG)SearchPtr++); + CopyrightPtr = WPDStringID; + + // continue compare as long as characters compare + // and not at end of string + + while ((Matchfound = (*CopyrightPtr++ == *StringPtr++)) && + (CopyrightPtr < WPDStringID + WPDStringIDLength)); + } + + // see if string was found (i.e., if this is a 3360) + + if (Matchfound) { + + // store system identifier ("3360") for later HAL use + + NCRPlatform = NCR3360; + + // read CPU good flags from CMOS and determine if MP + + ReadCMOS(0x88A, 1, &CpuFlags); + // *IsConfiguredMp = (CpuFlags & (CpuFlags-1)) ? TRUE : FALSE; + + // This is an MP hal + *IsConfiguredMp = TRUE; + + return(WPDPlatformName); + } + + } + + + // now check for an MSBU platform + + + /* + * Map in the BIOS text so we can look for our copyright string. + */ + BIOSPagePtr = (PVOID)((ULONG)MSBUCopyrightPhysicalPtr & + ~(PAGE_SIZE - 1)); + BIOSPagePtr = HalpMapPhysicalMemory(BIOSPagePtr, 2); + if (BIOSPagePtr == NULL) + return(NULL); + + StringPtr = (PUCHAR)((ULONG)BIOSPagePtr + + ((ULONG)MSBUCopyrightPhysicalPtr & (PAGE_SIZE - 1))) + + (MSBUCopyrightStringLen - 1); + CopyrightPtr = MSBUCopyrightString + (MSBUCopyrightStringLen - 1); + do { + Matchfound = ((*CopyrightPtr == '?') || + (*CopyrightPtr == *StringPtr)); + --CopyrightPtr; + --StringPtr; + } while (Matchfound && (CopyrightPtr >= MSBUCopyrightString)); + + // + // /* + // * Clear the mapping to BIOS. We mapped in two pages. + // */ + // BIOSPagePtr = MiGetPteAddress(BIOSPagePtr); + // *(PULONG)BIOSPagePtr = 0; + // *(((PULONG)BIOSPagePtr)+1) = 0; + // /* + // * Flush the TLB. + // */ + // _asm { + // mov eax, cr3 + // mov cr3, eax + // } + // + + if (Matchfound) { + /* + * must be an MSBU machine..determine which. + */ + ReadCMOS(0xB16, 4, (PUCHAR)&NCRPlatform); + for (MSBUPlatformMapPtr = MSBUPlatformMap; + (MSBUPlatformMapPtr->ClassFromFirmware != 0); + ++MSBUPlatformMapPtr) { + if (MSBUPlatformMapPtr->ClassFromFirmware == + NCRPlatform) { + + *IsConfiguredMp = TRUE; + return(MSBUPlatformMapPtr->PlatformName); + } + } + + /* + * prerelease version of firmware had this machine class + * at the wrong offset into CMOS. until all those versions + * of firmware are extinguished from the face of the earth + * we should recognize them with this: + */ + ReadCMOS(0xAB3, 4, (PUCHAR)&NCRPlatform); + for (MSBUPlatformMapPtr = MSBUPlatformMap; + (MSBUPlatformMapPtr->ClassFromFirmware != 0); + ++MSBUPlatformMapPtr) { + if (MSBUPlatformMapPtr->ClassFromFirmware == + NCRPlatform) { + *IsConfiguredMp = TRUE; + return(MSBUPlatformMapPtr->PlatformName); + } + } + } + + return(NULL); +} + + +#ifndef SETUP // if built with Hal, must provide ReadCMOS routine + +ULONG +HalpGetCmosData ( + IN ULONG SourceLocation, + IN ULONG SourceAddress, + IN PUCHAR Buffer, + IN ULONG Length); + +VOID +ReadCMOS( + IN ULONG StartingOffset, + IN ULONG Count, + IN PUCHAR ReturnValuePtr +) +/*++ + +Routine Description: + This routine simply converts a ReadCMOS call (a routine in setup) to + the corresponding routine provided in the Hal (HalpGetCmosData). + +--*/ +{ + HalpGetCmosData(1, StartingOffset, ReturnValuePtr, Count); +} +#endif diff --git a/private/ntos/nthals/halx86/i386/pcip.h b/private/ntos/nthals/halx86/i386/pcip.h new file mode 100644 index 000000000..c5f09f2e5 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/pcip.h @@ -0,0 +1,186 @@ +// +// Hal specific PCI bus structures +// + +typedef NTSTATUS +(*PciIrqRange) ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER PciSlot, + OUT PSUPPORTED_RANGE *Interrupt + ); + +typedef struct tagPCIPBUSDATA { + + // + // Defined PCI data + // + + PCIBUSDATA CommonData; + + // + // Implementation specific data + // + + union { + struct { + PULONG Address; + ULONG Data; + } Type1; + struct { + PUCHAR CSE; + PUCHAR Forward; + ULONG Base; + } Type2; + } Config; + + ULONG MaxDevice; + PciIrqRange GetIrqRange; + + BOOLEAN BridgeConfigRead; + UCHAR ParentBus; + UCHAR reserved[2]; + UCHAR SwizzleIn[4]; + + RTL_BITMAP DeviceConfigured; + ULONG ConfiguredBits[PCI_MAX_DEVICES * PCI_MAX_FUNCTION / 32]; +} PCIPBUSDATA, *PPCIPBUSDATA; + +#define PciBitIndex(Dev,Fnc) (Fnc*32 + Dev); + +#define PCI_CONFIG_TYPE(PciData) ((PciData)->HeaderType & ~PCI_MULTIFUNCTION) + +#define Is64BitBaseAddress(a) \ + (((a & PCI_ADDRESS_IO_SPACE) == 0) && \ + ((a & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)) + + +#if DBG +#define IRQXOR 0x2B +#else +#define IRQXOR 0 +#endif + + +// +// Prototypes for functions in ixpcibus.c +// + +VOID +HalpInitializePciBus ( + VOID + ); + +VOID +HalpReadPCIConfig ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + + +VOID +HalpWritePCIConfig ( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +PBUS_HANDLER +HalpAllocateAndInitPciBusHandler ( + IN ULONG HwType, + IN ULONG BusNo, + IN BOOLEAN TestAllocation + ); + + +// +// Prototypes for functions in ixpciint.c +// + +ULONG +HalpGetPCIIntOnISABus ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +VOID +HalpPCIAcquireType2Lock ( + PKSPIN_LOCK SpinLock, + PKIRQL Irql + ); + +VOID +HalpPCIReleaseType2Lock ( + PKSPIN_LOCK SpinLock, + KIRQL Irql + ); + +NTSTATUS +HalpAdjustPCIResourceList ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList + ); + +VOID +HalpPCIPin2ISALine ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciData + ); + +VOID +HalpPCIISALine2Pin ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciNewData, + IN PPCI_COMMON_CONFIG PciOldData + ); + +NTSTATUS +HalpGetISAFixedPCIIrq ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER PciSlot, + OUT PSUPPORTED_RANGE *Interrupt + ); + +// +// Prototypes for functions in ixpcibrd.c +// + +BOOLEAN +HalpGetPciBridgeConfig ( + IN ULONG HwType, + IN PUCHAR MaxPciBus + ); + +VOID +HalpFixupPciSupportedRanges ( + IN ULONG MaxBuses + ); + +// +// +// + +#ifdef SUBCLASSPCI + +VOID +HalpSubclassPCISupport ( + IN PBUS_HANDLER BusHandler, + IN ULONG HwType + ); + +#endif diff --git a/private/ntos/nthals/halx86/i386/xxbiosa.asm b/private/ntos/nthals/halx86/i386/xxbiosa.asm new file mode 100644 index 000000000..e3d5493b6 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/xxbiosa.asm @@ -0,0 +1,712 @@ +;++ +; +; Copyright (c) 1991 Microsoft Corporation +; +; Module Name: +; +; xxbiosa.asm +; +; Abstract: +; +; This implements the necessary code to put the processor into +; V86 mode, make a BIOS call, and return safely to protected mode. +; +; Author: +; +; John Vert (jvert) 29-Oct-1991 +; +; Environment: +; +; Kernel mode +; +; Notes: +; +; This module is intended for use in panic situations, such as a bugcheck. +; As a result, we cannot rely on the integrity of the system so we must +; handle everything ourselves. Notably, we must map our own memory by +; adding our own page tables and PTEs. +; +; We also cannot call KeBugCheck when we notice something has gone wrong. +; +; Revision History: +; +;-- +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc + .list + + extrn _DbgPrint:proc + EXTRNP _DbgBreakPoint,0,IMPORT + EXTRNP Kei386EoiHelper,0,IMPORT + + public _HalpRealModeStart + public _HalpRealModeEnd +; +; 32-bit override +; +OVERRIDE equ 66h + +; +; Reginfo structure +; + +RegInfo struc +RiSegSs dd 0 +RiEsp dd 0 +RiEFlags dd 0 +RiSegCs dd 0 +RiEip dd 0 +RiTrapFrame dd 0 +RiCsLimit dd 0 +RiCsBase dd 0 +RiCsFlags dd 0 +RiSsLimit dd 0 +RiSsBase dd 0 +RiSsFlags dd 0 +RiPrefixFlags dd 0 +RegInfo ends +REGINFOSIZE EQU 52 + +INT_NN_OPCODE EQU 0CDH + + page ,132 +_DATA SEGMENT DWORD PUBLIC 'DATA' + +; +; In order to return to the calling function after we've trapped out of +; V86 mode, we save our ESP value here. +; +HalpSavedEsp dd 0 + +_DATA ENDS + + +_TEXT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING + +if DBG + page ,132 + subttl "Processing Exception occurred in ABIOS code" +;++ +; VOID +; KiAbiosException ( +; VOID +; ) +; +; Routine Description: +; +; This routine is called after an exception being detected +; in ABIOS ROM code. The system will switch 16 stack to 32 bit +; stack and bugcheck. +; +; N.B. In fact this routine is simply used to resolve a reference +; to KiAbiosException routine in the Kimacro.inc ENTER_TRAP +; macro. +; +; +; Arguments: +; +; None. +; +; Return value: +; +; system stopped. +; +;-- + public _KiAbiosException +_KiAbiosException proc +_Ki16BitStackException: + ret + +_KiAbiosException endp + +endif + + +;++ +; ULONG +; HalpBorrowTss ( +; VOID +; ) +; +; Routine Description: +; +; This routine checks if the current TSS has IO MAP space. +; if yes, it simply returns. Otherwise, it switches to use +; the regular TSS. +; +; Arguments: +; +; None. +; +; Return value: +; +; Return original TSS selector if the regular Tss is borrowed by us. +; +;-- +cPublicProc _HalpBorrowTss, 0 +cPublicFpo 0, 0 + + xor eax, eax + str ax + mov edx, PCR[PcGdt] + add edx, eax ; (edx)->Gdt Entry of current + ; TSS + xor ecx, ecx + mov cl, [edx].KgdtLimitHi + shl ecx, 16 + mov cx, [edx].KgdtLimitLow ; (ecx) = TSS limit + cmp ecx, 2000H ; Is Io map space available? + ja short Hbt99 ; if a, yes, return + + sub edx, eax ; (edx)->GDT table + mov ch, [edx+KGDT_TSS+KgdtBaseHi] + mov cl, [edx+KGDT_TSS+KgdtBaseMid] + shl ecx, 16 + mov cx, [edx+KGDT_TSS+KgdtBaseLow] + mov PCR[PcTss], ecx + mov ecx, KGDT_TSS ; switch to use regular TSS + mov byte ptr [edx+KGDT_TSS+5], 089h ; 32bit, dpl=0, present, TSS32, + ; not busy. + ltr cx + stdRET _HalpBorrowTss ; (eax) = Original TSS sel + +Hbt99: + xor eax, eax ; No TSS swapped + stdRET _HalpBorrowTss + +stdENDP _HalpBorrowTss + + +;++ +; VOID +; HalpReturnTss ( +; ULONG TssSelector +; ) +; +; Routine Description: +; +; This routine switches the current TSS from regular TSS back to +; the panic TSS (NMI TSS or Double fault TSS). +; +; Arguments: +; +; TssSelector - the TSS selector to return to. +; +; Return value: +; +; None. +; +;-- +cPublicProc _HalpReturnTss, 1 +cPublicFpo 1, 0 + + mov edx, PCR[PcGdt] ; (edx)-> Gdt table + mov eax, [esp + 4] + and eax, 0FFFFh ; (eax)= New TSS sel + add edx, eax ; (edx)->Gdt Entry of new TSS + + mov ch, [edx+KgdtBaseHi] + mov cl, [edx+KgdtBaseMid] + shl ecx, 16 + mov cx, [edx+KgdtBaseLow] + mov PCR[PcTss], ecx + mov byte ptr [edx+5], 089h ; 32bit, dpl=0, present, TSS32, + ltr ax + stdRET _HalpReturnTss ; return and clear stack + +stdENDP _HalpReturnTss + +;++ +; +; VOID +; HalpBiosCall +; VOID +; ) +; +; Routine Description: +; +; This routine completes the transition to real mode, calls BIOS, and +; returns to protected mode. +; +; Arguments: +; +; None. +; +; Return Value: +; +; None. +; +;-- +;;ALIGN 4096 +cPublicProc _HalpBiosCall ,0 + + push ebp + mov ebp, esp + pushfd + push edi + push esi + push ebx + push ds + push es + push fs + push gs + push offset FLAT:HbcProtMode ; address where we will start + ; protected mode again once + ; V86 has completed. + mov HalpSavedEsp, esp + + mov eax, cr0 ; make sure alignment + and eax, not CR0_AM ; checks are disabled + mov cr0, eax + +; +; Create space for the V86 trap frame and update the ESP0 value in the TSS +; to use this space. We will set this up just below our current stack pointer. +; The stuff we push on the stack after we set ESP0 is irrelevant once we +; make it to V86 mode, so it's ok to blast it. +; + mov esi, fs:PcTss ; (esi) -> TSS + mov eax, esp + sub eax, NPX_FRAME_LENGTH ; skip FP save area + mov [esi]+TssEsp0, eax + + push dword ptr 0h ; V86 GS + push dword ptr 0h ; V86 FS + push dword ptr 0h ; V86 DS + push dword ptr 0h ; V86 ES + push dword ptr 2000h ; V86 SS + +; +; We calculate the V86 sp by adding the difference between the linear address +; of the V86 ip (HbcReal) and the linear address of the V86 sp (HbcV86Stack) +; to the offset of the V86 ip (HbcReal & 0xfff). +; + + mov eax, offset FLAT:HbcV86Stack-4 + sub eax, offset FLAT:HbcReal + mov edx, offset HbcReal + and edx, 0fffh + add eax, edx + push eax ; V86 esp + + pushfd + or dword ptr [esp], EFLAGS_V86_MASK; V86 eflags + or [esp], 03000h ; Give IOPL3 + push dword ptr 2000h ; V86 CS + mov eax, offset HbcReal + and eax, 0fffh + + push edx ; V86-mode EIP is offset + ; into CS. + iretd + +_HalpRealModeStart label byte + +HbcReal: + db OVERRIDE ; make mov 32-bits + mov eax, 03h ; 80x25 mode, 16 colors + int 10h + + db OVERRIDE ; make mov 32-bits + mov eax, 1112h ; use 8x8 font (causes 50 line mode) + db OVERRIDE + mov ebx, 0 + int 10h + + db 0c4h, 0c4h ; BOP to indicate V86 mode is done. + +; +; V86-mode stack +; +align 4 + db 2048 dup(0) +HbcV86Stack: + +_HalpRealModeEnd label byte + +HbcProtMode: +; +; We are back from V86 mode, so restore everything we saved and we are done. +; + pop gs + pop fs + pop es + pop ds + pop ebx + pop esi + pop edi + popfd + pop ebp + stdRET _HalpBiosCall + + public _HalpBiosCallEnd +_HalpBiosCallEnd label byte + + + +_HalpBiosCall endp + + + subttl "HAL General Protection Fault" +;++ +; +; Routine Description: +; +; Handle General protection fault. +; +; This fault handler is used by the HAL for V86 mode faults only. +; It should NEVER be used except when running in V86 mode. The HAL +; replaces the general-purpose KiTrap0D handler entry in the IDT with +; this routine. This allows us to emulate V86-mode instructions which +; cause a fault. After we return from V86 mode, we can restore the +; KiTrap0D handler in the IDT. +; +; Arguments: +; +; At entry, the saved CS:EIP point to the faulting instruction +; Error code (whose value depends on detected condition) is provided. +; +; Return value: +; +; None +; +;-- + ASSUME DS:FLAT, SS:NOTHING, ES:FLAT + + ENTER_DR_ASSIST Htd_a, Htd_t, NoAbiosAssist +cPublicProc _HalpTrap0D ,0 + + ENTER_TRAP Htd_a, Htd_t + +; +; Did the trap occur in V86 mode? If not, something is completely screwed. +; + test dword ptr [ebp]+TsEFlags,00020000H + jnz Ht0d10 + +; +; The trap was not from V86 mode, so something is very wrong. We cannot +; BugCheck, since we are probably already in a BugCheck. So just stop. +; + +if DBG +_DATA segment +MsgBadHalTrap db 'HAL: Trap0D while not in V86 mode',0ah,0dh,0 +_DATA ends + + push offset FLAT:MsgBadHalTrap + call _DbgPrint + add esp,4 + stdCall _DbgBreakPoint +endif +; +; We can't bugcheck, so just commit suicide. Maybe we should reboot? +; + jmp $ + +Ht0d10: + stdCall HalpDispatchV86Opcode + SPURIOUS_INTERRUPT_EXIT +stdENDP _HalpTrap0d + + subttl "HAL Invalid Opcode Fault" +;++ +; +; Routine Description: +; +; Handle invalid opcode fault +; +; This fault handler is used by the HAL to indicate when V86 mode +; execution is finished. The V86 code attempts to execute an invalid +; instruction (BOP) when it is done, and that brings us here. +; This routine just removes the trap frame from the stack and does +; a RET. Note that this assumes that ESP0 in the TSS has been set +; up to point to the top of the stack that we want to be running on +; when the V86 call has completed. +; +; This should NEVER be used except when running in V86 mode. The HAL +; replaces the general-purpose KiTrap06 handler entry in the IDT with +; this routine. It also sets up ESP0 in the TSS appropriately. After +; the V86 call has completed, it restores these to their previous values. +; +; Arguments: +; +; At entry, the saved CS:EIP point to the faulting instruction +; Error code (whose value depends on detected condition) is provided. +; +; Return value: +; +; None +; +;-- + ASSUME DS:FLAT, SS:NOTHING, ES:FLAT + +cPublicProc _HalpTrap06 ,0 + mov eax,KGDT_R3_DATA OR RPL_MASK + mov ds,ax + mov es,ax + mov esp, HalpSavedEsp + ret + +stdENDP _HalpTrap06 + + subttl "Instruction Emulation Dispatcher" +;++ +; +; Routine Description: +; +; This routine dispatches to the opcode specific emulation routine, +; based on the first byte of the opcode. Two byte opcodes, and prefixes +; result in another level of dispatching, from the handling routine. +; +; This code blatantly stolen from ke\i386\instemul.asm +; +; Arguments: +; +; ebp = pointer to trap frame +; +; Returns: +; +; Nothing +; + +cPublicProc HalpDispatchV86Opcode ,0 + +RI equ [ebp - REGINFOSIZE] + push ebp + mov ebp,esp + sub esp,REGINFOSIZE + push esi + push edi + + ; Initialize RegInfo + + mov esi,[ebp] + mov RI.RiTrapFrame,esi + movzx eax,word ptr [esi].TsHardwareSegSs + mov RI.RiSegSs,eax + mov eax,[esi].TsHardwareEsp + mov RI.RiEsp,eax + mov eax,[esi].TsEFlags + mov RI.RiEFlags,eax + movzx eax,word ptr [esi].TsSegCs + mov RI.RiSegCs,eax + mov eax,[esi].TsEip + mov RI.RiEip,eax + + xor eax,eax + mov RI.RiPrefixFlags,eax + lea esi,RI + +; +; Convert CS to a linear address +; + + mov eax,[esi].RiSegCs + shl eax,4 + mov [esi].RiCsBase,eax + mov [esi].RiCsLimit,0FFFFh + mov [esi].RiCsFlags,0 + + mov edi,RI.RiEip + cmp edi,RI.RiCsLimit + ja doerr + + add edi,RI.RiCsBase + mov dl, [edi] ; get faulting opcode + cmp dl, INT_NN_OPCODE + je short @f + + stdCall HalpOpcodeInvalid + jmp short doerr + +@@: + stdCall HalpOpcodeINTnn + test eax,0FFFFh + jz do20 + + mov edi,RI.RiTrapFrame + mov eax,RI.RiEip ; advance eip + mov [edi].TsEip,eax + mov eax,1 +do20: pop edi + pop esi + mov esp,ebp + pop ebp + ret + +doerr: xor eax,eax + jmp do20 +stdENDP HalpDispatchV86Opcode + + page ,132 + subttl "Invalid Opcode Handler" +;++ +; +; Routine Description: +; +; This routine handles invalid opcodes. It prints the invalid +; opcode message, and breaks into the kernel debugger. +; +; Arguments: +; +; esi = address of reg info +; edx = opcode +; +; Returns: +; +; nothing +; + +cPublicProc HalpOpcodeInvalid ,0 + +_DATA segment +HalpMsgInvalidOpcode db 'HAL: An invalid V86 opcode was encountered at ' + db 'address %x:%x',0ah, 0dh, 0 +_DATA ends + + push [esi].RiEip + push [esi].RiSegCs + push offset FLAT:HalpMsgInvalidOpcode + call _DbgPrint ; display invalid opcode message + add esp,12 + int 3 + xor eax,eax + stdRET HalpOpcodeInvalid + +stdENDP HalpOpcodeInvalid + + subttl "INTnn Opcode Handler" +;++ +; +; Routine Description: +; +; This routine emulates an INTnn opcode. It retrieves the handler +; from the IVT, pushes the current cs:ip and flags on the stack, +; and dispatches to the handler. +; +; Arguments: +; +; esi = address of reg info +; edx = opcode +; +; Returns: +; +; Current CS:IP on user stack +; RiCs:RiEip -> handler from IVT +; + +cPublicProc HalpOpcodeINTnn ,0 + + push ebp + push edi + push ebx + +; +; Convert SS to linear address +; + mov eax,[esi].RiSegSs + shl eax,4 + mov [esi].RiSsBase,eax + mov [esi].RiSsLimit,0FFFFh + mov [esi].RiSsFlags,0 + + inc [esi].RiEip ; point to int # + mov edi,[esi].RiEip + cmp edi,[esi].RiCsLimit + ja oinerr + + add edi,[esi].RiCsBase + movzx ecx,byte ptr [edi] ; get int # + inc [esi].RiEip ; inc past end of instruction + stdCall HalpPushInt + test eax,0FFFFh + jz oin20 ; error! +; +; BugBug Some sort of check for BOP should go here, or in push int. +; + + mov ebp,[esi].RiTrapFrame + mov eax,[esi].RiSegSs + mov [ebp].TsHardwareSegSs,eax + mov eax,[esi].RiEsp + mov [ebp].TsHardwareEsp,eax + mov eax,[esi].RiSegCs + mov [ebp].TsSegCs,eax + mov eax,[esi].RiEFlags + mov [ebp].TsEFlags,eax + mov eax,1 +oin20: pop ebx + pop edi + pop ebp + stdRET HalpOpcodeINTnn + +oinerr: xor eax,eax + jmp oin20 + +stdENDP HalpOpcodeINTnn + + page ,132 + subttl "Push Interrupt frame on user stack" +;++ +; +; Routine Description: +; +; This routine pushes an interrupt frame on the user stack +; +; Arguments: +; +; ecx = interrupt # +; esi = address of reg info +; Returns: +; +; interrupt frame pushed on stack +; reg info updated +;bugbug does this routine trash BX?? +; +cPublicProc HalpPushInt ,0 + push ebx + + mov edx,[esi].RiEsp + mov ebx,[esi].RiSsBase + and edx,0FFFFh ; only use a 16 bit sp + sub dx,2 + mov ax,word ptr [esi].RiEFlags + mov [ebx+edx],ax ; push flags + sub dx,2 + mov ax,word ptr [esi].RiSegCs + mov [ebx+edx],ax ; push cs + sub dx,2 + mov ax,word ptr [esi].RiEip + mov [ebx+edx],ax ; push ip + mov eax,[ecx*4] ; get new cs:ip value + push eax + movzx eax,ax + mov [esi].RiEip,eax + pop eax + shr eax,16 + mov [esi].RiSegCs,eax + mov word ptr [esi].RiEsp,dx + +; +; Convert CS to a linear address +; + + mov eax,[esi].RiSegCs + shl eax,4 + mov [esi].RiCsBase,eax + mov [esi].RiCsLimit,0FFFFh + mov [esi].RiCsFlags,0 + + mov eax,1 ; success +pi80: pop ebx + stdRET HalpPushInt +stdENDP HalpPushInt + + +_TEXT ends + end diff --git a/private/ntos/nthals/halx86/i386/xxbiosc.c b/private/ntos/nthals/halx86/i386/xxbiosc.c new file mode 100644 index 000000000..6b5f1cc69 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/xxbiosc.c @@ -0,0 +1,299 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + xxbiosc.c + +Abstract: + + This module implements the protect-mode routines necessary to make the + transition to real mode and return to protected mode. + +Author: + + John Vert (jvert) 29-Oct-1991 + + +Environment: + + Kernel mode only. + Probably a panic-stop, so we cannot use any system services. + +Revision History: + +--*/ +#include "halp.h" + +// +// Function definitions +// + + +ULONG +HalpBorrowTss( + VOID + ); + +VOID +HalpReturnTss( + ULONG TssSelector + ); + +VOID +HalpBiosCall( + VOID + ); + + +VOID +HalpTrap06( + VOID + ); + + +VOID +HalpTrap0D( + VOID + ); + +VOID +HalpBiosDisplayReset( + VOID + ) + +/*++ + +Routine Description: + + Calls BIOS by putting the machine into V86 mode. This involves setting up + a physical==virtual identity mapping for the first 1Mb of memory, setting + up V86-specific trap handlers, and granting I/O privilege to the V86 + process by editing the IOPM bitmap in the TSS. + +Environment: + + Interrupts disabled. + +Arguments: + + None + +Return Value: + + None. + +--*/ + +{ + HARDWARE_PTE OldPageTable; + USHORT OldIoMapBase; + ULONG OldEsp0; + PHARDWARE_PTE Pte; + PHARDWARE_PTE V86CodePte; + ULONG cnt; + ULONG OldTrap0DHandler; + ULONG OldTrap06Handler; + PUCHAR IoMap; + ULONG Virtual; +// KIRQL OldIrql; + ULONG OriginalTssSelector; + extern PVOID HalpRealModeStart; + extern PVOID HalpRealModeEnd; +PHARDWARE_PTE PointerPde; +ULONG PageFrame; +ULONG PageFrameEnd; + + // + // Interrupts are off, but V86 mode might turn them back on again. + // + HalpDisableAllInterrupts (); + + // + // We need to set up an identity mapping in the first page table. First, + // we save away the old page table. + // + OldPageTable = *MiGetPdeAddress(0); + + // + // Now we put the HAL page table into the first slot of the page + // directory. Note that this page table is now the first and last + // entries in the page directory. + // + Pte = MiGetPdeAddress(0); + Pte->PageFrameNumber = MiGetPdeAddress(0xffc00000)->PageFrameNumber; + Pte->Valid = 1; + Pte->Write = 1; + Pte->Owner = 1; // User-accessible + + // + // Flush TLB + // + + HalpFlushTLB(); + + // + // Map the first 1Mb of virtual memory to the first 1Mb of physical + // memory + // + for (Virtual=0; Virtual < 0x100000; Virtual += PAGE_SIZE) { + Pte = MiGetPteAddress(Virtual); + Pte->PageFrameNumber = ((ULONG)Virtual >> PAGE_SHIFT); + Pte->Valid = 1; + Pte->Write = 1; + Pte->Owner = 1; // User-accessible + } + + // + // Map our code into the virtual machine + // + + Pte = MiGetPteAddress(0x20000); + PointerPde = MiGetPdeAddress(&HalpRealModeStart); + + if ( PointerPde->LargePage ) { + // + // Map real mode PTEs into virtual mapping. The source PDE is + // from the indenity large pte map, so map the virtual machine PTEs + // based on the base of the large PDE frame. + // + + PageFrame = ((ULONG)(&HalpRealModeStart) >> 12) & 0x3FF; + PageFrameEnd = ((ULONG)(&HalpRealModeEnd) >> 12) & 0x3FF; + do { + + Pte->PageFrameNumber = PointerPde->PageFrameNumber + PageFrame; + + ++Pte; + ++PageFrame; + + } while (PageFrame <= PageFrameEnd); + + } else { + + // + // Map real mode PTEs into virtual machine PTEs, by copying the + // page frames from the source to the virtual machine PTEs. + // + + V86CodePte = MiGetPteAddress(&HalpRealModeStart); + do { + Pte->PageFrameNumber = V86CodePte->PageFrameNumber; + + ++Pte; + ++V86CodePte; + + } while ( V86CodePte <= MiGetPteAddress(&HalpRealModeEnd) ); + + } + // + // Flush TLB + // + + HalpFlushTLB(); + + // + // We need to replace the current TRAP D handler with our own, so + // we can do instruction emulation for V86 mode + // + + OldTrap0DHandler = KiReturnHandlerAddressFromIDT(0xd); + KiSetHandlerAddressToIDT(0xd, HalpTrap0D); + + OldTrap06Handler = KiReturnHandlerAddressFromIDT(6); + KiSetHandlerAddressToIDT(6, HalpTrap06); + + // + // Make sure current TSS has IoMap space available. If no, borrow + // Normal TSS. + // + + OriginalTssSelector = HalpBorrowTss(); + + // + // Overwrite the first access map with zeroes, so the V86 code can + // party on all the registers. + // + IoMap = (PUCHAR)&(KeGetPcr()->TSS->IoMaps[0]); + + for (cnt=0; cntTSS->IoMapBase; + + KeGetPcr()->TSS->IoMapBase = KiComputeIopmOffset(1); + + // + // Save the current ESP0, as HalpBiosCall() trashes it. + // + OldEsp0 = KeGetPcr()->TSS->Esp0; + + // + // Call the V86-mode code + // + HalpBiosCall(); + + // + // Restore the TRAP handlers + // + + KiSetHandlerAddressToIDT(0xd, OldTrap0DHandler); + KiSetHandlerAddressToIDT(6, OldTrap06Handler); + + // + // Restore Esp0 value + // + KeGetPcr()->TSS->Esp0 = OldEsp0; + + KeGetPcr()->TSS->IoMapBase = OldIoMapBase; + + // + // Return borrowed TSS if any. + // + + if (OriginalTssSelector != 0) { + HalpReturnTss(OriginalTssSelector); + } + + // + // Unmap the first 1Mb of virtual memory + // + for (Virtual = 0; Virtual < 0x100000; Virtual += PAGE_SIZE) { + Pte = MiGetPteAddress(Virtual); + Pte->PageFrameNumber = 0; + Pte->Valid = 0; + Pte->Write = 0; + } + + // + // Restore the original page table that we replaced. + // + *MiGetPdeAddress(0) = OldPageTable; + + // + // Flush TLB + // + + HalpFlushTLB(); + + // + // This function is only used during a system crash. We don't re- + // enable interrupts. + // + // KeLowerIrql(OldIrql); +} + +HAL_DISPLAY_BIOS_INFORMATION +HalpGetDisplayBiosInformation ( + VOID + ) +{ + // this hal uses native int-10 + + return HalDisplayInt10Bios; +} + diff --git a/private/ntos/nthals/halx86/i386/xxdisp.c b/private/ntos/nthals/halx86/i386/xxdisp.c new file mode 100644 index 000000000..5806751fc --- /dev/null +++ b/private/ntos/nthals/halx86/i386/xxdisp.c @@ -0,0 +1,476 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + xxdisp.c + +Abstract: + + This module implements the HAL display initialization and output routines + for a x86 system. + +Author: + + David N. Cutler (davec) 27-Apr-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "halp.h" + +// +// Private function prototypes +// +VOID +HalpClearDisplay( + VOID + ); + +VOID +HalpNextLine( + VOID + ); + +VOID +HalpScrollDisplay( + VOID + ); + +VOID +HalpPutCharacter( + IN UCHAR Character + ); + +#define REVERSE_ATTRIBUTE 0x17 +#define ROWS 50 +#define COLS 80 + +ULONG HalpCursorX=0; +ULONG HalpCursorY=0; + +KSPIN_LOCK HalpDisplayLock; + + +PUSHORT VideoBuffer; + +// +// If someone calls HalDisplayString before HalInitSystem, we need to be +// able to put something up on screen anyway. +// +BOOLEAN HalpDisplayInitialized=FALSE; + +// +// This is how we tell if GDI has taken over the display. If so, we are +// in graphics mode and we need to reset the display to text mode before +// displaying anything. (Panic stop) +// +BOOLEAN HalpOwnsDisplay=TRUE; +PHAL_RESET_DISPLAY_PARAMETERS HalpResetDisplayParameters; + +BOOLEAN HalpDoingCrashDump = FALSE; + + +VOID +HalAcquireDisplayOwnership ( + IN PHAL_RESET_DISPLAY_PARAMETERS ResetDisplayParameters + ) + +/*++ + +Routine Description: + + This routine switches ownership of the display away from the HAL to + the system display driver. It is called when the system has reached + a point during bootstrap where it is self supporting and can output + its own messages. Once ownership has passed to the system display + driver any attempts to output messages using HalDisplayString must + result in ownership of the display reverting to the HAL and the + display hardware reinitialized for use by the HAL. + +Arguments: + + ResetDisplayParameters - if non-NULL the address of a function + the hal can call to reset the video card. The function returns + TRUE if the display was reset. + +Return Value: + + None. + +--*/ + +{ + HalpResetDisplayParameters=ResetDisplayParameters; + HalpOwnsDisplay=FALSE; + return; +} + +VOID +HalpVideoReboot() +{ + if (HalpResetDisplayParameters && !HalpOwnsDisplay) { + // + // Video work-around. The video driver has a reset function, + // call it before resetting the system in case the bios doesn't + // know how to reset the displays video mode. + // + + if (HalpResetDisplayParameters(COLS, ROWS)) { + // display was reset, make sure it's blank + HalpClearDisplay(); + } + } +} + + + +VOID +HalpInitializeDisplay( + VOID + ) + +/*++ + +Routine Description: + + Initializes the VGA display. This uses HalpMapPhysicalMemory to map + the video buffer at 0xb8000 - 0xba000 into high virtual memory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + if (HalpDisplayInitialized == FALSE) { + + HalpDisplayInitialized = TRUE; + + KeInitializeSpinLock(&HalpDisplayLock); + + // + // If somebody called HalDisplayString before Phase 0 initialization, + // the video buffer has already been mapped and cleared, and a + // message has already been displayed. So we don't want to clear + // the screen again, or map the screen again. + // + + // + // Map two pages of memory starting at physical address 0xb8000. + // + + VideoBuffer = (PUSHORT)HalpMapPhysicalMemory((PVOID)0xb8000,2); + + HalpClearDisplay(); + } +} + +VOID +HalDisplayString ( + PUCHAR String + ) + +/*++ + +Routine Description: + + This routine displays a character string on the display screen. + +Arguments: + + String - Supplies a pointer to the characters that are to be displayed. + +Return Value: + + None. + +--*/ + +{ + if (!HalpDisplayInitialized && HalpOwnsDisplay) { + + // + // If somebody has called HalDisplayString before Phase 0 + // initialization, we need to make sure we get our message out + // anyway. So we initialize the display before HalInitSystem does. + // HalpInitializeDisplay is smart enough to only map the video + // buffer and clear the screen the first time it is called. + // + + HalpInitializeDisplay(); + } + + // + // Synchronize access to the display so that MP systems won't + // get garbage output due to simultaneous calls. It also prevents + // two processors from attempting to call BIOS and reset the display + // simultaneously. + // + + KiAcquireSpinLock(&HalpDisplayLock); + + if (HalpOwnsDisplay == FALSE) { + + // + // The display has been put in graphics mode, and we need to + // reset it to text mode before we can display any text on it. + // + + if (HalpResetDisplayParameters) { + HalpOwnsDisplay = HalpResetDisplayParameters(COLS, ROWS); + } + + if (HalpOwnsDisplay == FALSE) { + HalpBiosDisplayReset(); + } + + HalpOwnsDisplay = TRUE; + HalpDoingCrashDump = TRUE; + HalpClearDisplay(); + } + + while (*String) { + + switch (*String) { + case '\n': + HalpNextLine(); + break; + case '\r': + HalpCursorX = 0; + break; + default: + HalpPutCharacter(*String); + if (++HalpCursorX == COLS) { + HalpNextLine(); + } + } + ++String; + } + + KiReleaseSpinLock(&HalpDisplayLock); + return; +} + +VOID +HalpDisplayDebugStatus ( + PUCHAR str, + ULONG len + ) +{ + PUSHORT p; + + if (!HalpDisplayInitialized || !HalpOwnsDisplay) { + return; + } + + for (p = &VideoBuffer [COLS - len]; len; str++, p++, len--) { + *p = (USHORT)((REVERSE_ATTRIBUTE << 8) | *str); + } +} + +VOID +HalQueryDisplayParameters ( + OUT PULONG WidthInCharacters, + OUT PULONG HeightInLines, + OUT PULONG CursorColumn, + OUT PULONG CursorRow + ) +/*++ + +Routine Description: + + This routine return information about the display area and current + cursor position. + +Arguments: + + WidthInCharacter - Supplies a pointer to a varible that receives + the width of the display area in characters. + + HeightInLines - Supplies a pointer to a variable that receives the + height of the display area in lines. + + CursorColumn - Supplies a pointer to a variable that receives the + current display column position. + + CursorRow - Supplies a pointer to a variable that receives the + current display row position. + +Return Value: + + None. + +--*/ +{ + *WidthInCharacters = COLS; + *HeightInLines = ROWS; + *CursorColumn = HalpCursorX; + *CursorRow = HalpCursorX; + +} + +VOID +HalSetDisplayParameters ( + IN ULONG CursorColumn, + IN ULONG CursorRow + ) +/*++ + +Routine Description: + + This routine set the current cursor position on the display area. + +Arguments: + + CursorColumn - Supplies the new display column position. + + CursorRow - Supplies a the new display row position. + +Return Value: + + None. + +--*/ +{ + HalpCursorX = CursorColumn >= COLS ? COLS-1 : CursorColumn; + HalpCursorY = CursorRow >= ROWS ? ROWS-1 : CursorRow; +} + +VOID +HalpNextLine( + VOID + ) + +/*++ + +Routine Description: + + Moves the cursor to the start of the next line, scrolling if necessary. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + if (HalpCursorY==ROWS-1) { + HalpScrollDisplay(); + } else { + ++HalpCursorY; + } + HalpCursorX = 0; +} + +VOID +HalpScrollDisplay( + VOID + ) + +/*++ + +Routine Description: + + Scrolls the text on the display up one line. + +Arguments: + + None + +Return Value: + + None. + +--*/ + +{ + PUSHORT NewStart; + ULONG i; + + + NewStart = VideoBuffer+COLS; + RtlMoveMemory(VideoBuffer, NewStart, (ROWS-1)*COLS*sizeof(USHORT)); + + for (i=(ROWS-1)*COLS; iLoadOptions != NULL) { + Options = LoaderBlock->LoadOptions; + + // + // Check if PCI settings are locked down + // + + if (strstr(Options, HalpSzPciLock)) { + HalpPciLockSettings = TRUE; + } + + // + // Has the user asked for an initial BreakPoint? + // + + if (strstr(Options, HalpSzBreak)) { + DbgBreakPoint(); + } + } + + return; +} + + + +BOOLEAN +HalInitSystem ( + IN ULONG Phase, + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ) + + +/*++ + +Routine Description: + + This function initializes the Hardware Architecture Layer (HAL) for an + x86 system. + +Arguments: + + None. + +Return Value: + + A value of TRUE is returned is the initialization was successfully + complete. Otherwise a value of FALSE is returend. + +--*/ + +{ + PMEMORY_ALLOCATION_DESCRIPTOR Descriptor; + PLIST_ENTRY NextMd; + KIRQL CurrentIrql; + PKPRCB pPRCB; + + pPRCB = KeGetCurrentPrcb(); + + if (Phase == 0) { + + HalpBusType = LoaderBlock->u.I386.MachineType & 0x00ff; + HalpGetParameters (LoaderBlock); + + // + // Verify Prcb version and build flags conform to + // this image + // + +#if DBG + if (!(pPRCB->BuildType & PRCB_BUILD_DEBUG)) { + // This checked hal requires a checked kernel + KeBugCheckEx (MISMATCHED_HAL, + 2, pPRCB->BuildType, PRCB_BUILD_DEBUG, 0); + } +#else + if (pPRCB->BuildType & PRCB_BUILD_DEBUG) { + // This free hal requires a free kernel + KeBugCheckEx (MISMATCHED_HAL, 2, pPRCB->BuildType, 0, 0); + } +#endif +#ifndef NT_UP + if (pPRCB->BuildType & PRCB_BUILD_UNIPROCESSOR) { + // This MP hal requires an MP kernel + KeBugCheckEx (MISMATCHED_HAL, 2, pPRCB->BuildType, 0, 0); + } +#endif + if (pPRCB->MajorVersion != PRCB_MAJOR_VERSION) { + KeBugCheckEx (MISMATCHED_HAL, + 1, pPRCB->MajorVersion, PRCB_MAJOR_VERSION, 0); + } + + // + // Phase 0 initialization + // only called by P0 + // + + // + // Check to make sure the MCA HAL is not running on an ISA/EISA + // system, and vice-versa. + // +#if MCA + if (HalpBusType != MACHINE_TYPE_MCA) { + KeBugCheckEx (MISMATCHED_HAL, + 3, HalpBusType, MACHINE_TYPE_MCA, 0); + } +#else + if (HalpBusType == MACHINE_TYPE_MCA) { + KeBugCheckEx (MISMATCHED_HAL, + 3, HalpBusType, 0, 0); + } +#endif + + HalpInitializePICs(); + + // + // Now that the PICs are initialized, we need to mask them to + // reflect the current Irql + // + + CurrentIrql = KeGetCurrentIrql(); + CurrentIrql = KfRaiseIrql(CurrentIrql); + + // + // Initialize CMOS + // + + HalpInitializeCmos(); + + // + // Fill in handlers for APIs which this hal supports + // + + HalQuerySystemInformation = HaliQuerySystemInformation; + HalSetSystemInformation = HaliSetSystemInformation; + + // + // Register cascade vector + // + + HalpRegisterVector ( + InternalUsage, + PIC_SLAVE_IRQ + PRIMARY_VECTOR_BASE, + PIC_SLAVE_IRQ + PRIMARY_VECTOR_BASE, + HIGH_LEVEL ); + + // + // Register base IO space used by hal + // + + HalpRegisterAddressUsage (&HalpDefaultPcIoSpace); + if (HalpBusType == MACHINE_TYPE_EISA) { + HalpRegisterAddressUsage (&HalpEisaIoSpace); + } + + // + // Note that HalpInitializeClock MUST be called after + // HalpInitializeStallExecution, because HalpInitializeStallExecution + // reprograms the timer. + // + + HalpInitializeStallExecution(0); + + // + // Setup the clock + // + + HalpInitializeClock(); + + // + // Make sure profile is disabled + // + + HalStopProfileInterrupt(0); + + HalpInitializeDisplay(); + + // + // Initialize spinlock used by HalGetBusData hardware access routines + // + + KeInitializeSpinLock(&HalpSystemHardwareLock); + + // + // Determine if there is physical memory above 16 MB. + // + + LessThan16Mb = TRUE; + + NextMd = LoaderBlock->MemoryDescriptorListHead.Flink; + + while (NextMd != &LoaderBlock->MemoryDescriptorListHead) { + Descriptor = CONTAINING_RECORD( NextMd, + MEMORY_ALLOCATION_DESCRIPTOR, + ListEntry ); + + if (Descriptor->MemoryType != LoaderFirmwarePermanent && + Descriptor->MemoryType != LoaderSpecialMemory && + Descriptor->BasePage + Descriptor->PageCount > 0x1000) { + LessThan16Mb = FALSE; + break; + } + + NextMd = Descriptor->ListEntry.Flink; + } + + // + // Determine the size need for map buffers. If this system has + // memory with a physical address of greater than + // MAXIMUM_PHYSICAL_ADDRESS, then allocate a large chunk; otherwise, + // allocate a small chunk. + // + + if (LessThan16Mb) { + + // + // Allocate a small set of map buffers. They are only need for + // slave DMA devices. + // + + HalpMapBufferSize = INITIAL_MAP_BUFFER_SMALL_SIZE; + + } else { + + // + // Allocate a larger set of map buffers. These are used for + // slave DMA controllers and Isa cards. + // + + HalpMapBufferSize = INITIAL_MAP_BUFFER_LARGE_SIZE; + + } + + // + // Allocate map buffers for the adapter objects + // + + HalpMapBufferPhysicalAddress.LowPart = + HalpAllocPhysicalMemory (LoaderBlock, MAXIMUM_PHYSICAL_ADDRESS, + HalpMapBufferSize >> PAGE_SHIFT, TRUE); + HalpMapBufferPhysicalAddress.HighPart = 0; + + + if (!HalpMapBufferPhysicalAddress.LowPart) { + + // + // There was not a satisfactory block. Clear the allocation. + // + + HalpMapBufferSize = 0; + } + + } else { + + // + // Phase 1 initialization + // + + if (pPRCB->Number == 0) { + + // + // If P0, then setup global vectors + // + + HalpRegisterInternalBusHandlers (); + + // + // Set feature bits + // + + HalpFeatureBits = HalpGetFeatureBits(); + + HalpEnableInterruptHandler ( + DeviceUsage, // Report as device vector + V2I (CLOCK_VECTOR), // Bus interrupt level + CLOCK_VECTOR, // System IDT + CLOCK2_LEVEL, // System Irql + HalpClockInterrupt, // ISR + Latched ); + + HalpEnableInterruptHandler ( + DeviceUsage, // Report as device vector + V2I (PROFILE_VECTOR), // Bus interrupt level + PROFILE_VECTOR, // System IDT + PROFILE_LEVEL, // System Irql + HalpProfileInterrupt, // ISR + Latched ); + + // + // If 486, the FP error will be routed via trap10. So we + // don't enable irq13. Otherwise (CPU=386), we will enable irq13 + // to handle FP error. + // + + if (pPRCB->CpuType == 3) { + HalpEnableInterruptHandler ( + DeviceUsage, // Report as device vector + V2I (I386_80387_VECTOR), // Bus interrupt level + I386_80387_VECTOR, // System IDT + I386_80387_IRQL, // System Irql + HalpIrq13Handler, // ISR + Latched ); + } + } + + } + + +#ifndef NT_UP + HalpInitMP (Phase, LoaderBlock); +#endif + + return TRUE; +} + +ULONG +HalpGetFeatureBits ( + VOID + ) +{ + UCHAR Buffer[50]; + ULONG Junk, ProcessorFeatures, Bits; + PKPRCB Prcb; + + Bits = 0; + + Prcb = KeGetCurrentPrcb(); + + if (!Prcb->CpuID) { + return Bits; + } + + // + // Determine the processor type + // + + HalpCpuID (0, &Junk, (PULONG) Buffer+0, (PULONG) Buffer+2, (PULONG) Buffer+1); + Buffer[12] = 0; + + // + // If this is an Intel processor, determine whichNT compatible + // features are present + // + + if (strcmp (Buffer, HalpGenuineIntel) == 0) { + + HalpCpuID (1, &Junk, &Junk, &Junk, &ProcessorFeatures); + + // + // Check Intel feature bits for HAL features needed + // + + if (Prcb->CpuType == 6) { + Bits |= HAL_PERF_EVENTS; + } + + if (Prcb->CpuType < 6) { + Bits |= HAL_NO_SPECULATION; + } + + if (ProcessorFeatures & CPUID_MCA_MASK) { + Bits |= HAL_MCA_PRESENT; + } + + if (ProcessorFeatures & CPUID_MCE_MASK) { + Bits |= HAL_MCE_PRESENT; + } + + } + + return Bits; +} diff --git a/private/ntos/nthals/halx86/i386/xxioacc.asm b/private/ntos/nthals/halx86/i386/xxioacc.asm new file mode 100644 index 000000000..8d53fe90a --- /dev/null +++ b/private/ntos/nthals/halx86/i386/xxioacc.asm @@ -0,0 +1,386 @@ + title "ix ioaccess" +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; ixioacc.asm +; +; Abstract: +; +; Procedures to correctly touch I/O registers. +; +; Author: +; +; Bryan Willman (bryanwi) 16 May 1990 +; +; Environment: +; +; User or Kernel, although privledge (IOPL) may be required. +; +; Revision History: +; +;-- + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros + .list + +_TEXT$00 SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; I/O port space read and write functions. +; +; These have to be actual functions on the 386, because we need +; to use assembler, but cannot return a value if we inline it. +; +; This set of functions manipulates I/O registers in PORT space. +; (Uses x86 in and out instructions) +; +; WARNING: Port addresses must always be in the range 0 to 64K, because +; that's the range the hardware understands. +; +;-- + + + +;++ +; +; UCHAR +; READ_PORT_UCHAR( +; PUCHAR Port +; ) +; +; Arguments: +; (esp+4) = Port +; +; Returns: +; Value in Port. +; +;-- +cPublicProc _READ_PORT_UCHAR ,1 +cPublicFpo 1, 0 + + xor eax, eax ; Eliminate partial stall on return to caller + + mov edx,[esp+4] ; (dx) = Port + in al,dx + stdRET _READ_PORT_UCHAR + +stdENDP _READ_PORT_UCHAR + + + +;++ +; +; USHORT +; READ_PORT_USHORT( +; PUSHORT Port +; ) +; +; Arguments: +; (esp+4) = Port +; +; Returns: +; Value in Port. +; +;-- +cPublicProc _READ_PORT_USHORT ,1 +cPublicFpo 1, 0 + + xor eax, eax ; Eliminate partial stall on return to caller + + mov edx,[esp+4] ; (dx) = Port + in ax,dx + stdRET _READ_PORT_USHORT + +stdENDP _READ_PORT_USHORT + + + +;++ +; +; ULONG +; READ_PORT_ULONG( +; PULONG Port +; ) +; +; Arguments: +; (esp+4) = Port +; +; Returns: +; Value in Port. +; +;-- +cPublicProc _READ_PORT_ULONG ,1 +cPublicFpo 1, 0 + + mov edx,[esp+4] ; (dx) = Port + in eax,dx + stdRET _READ_PORT_ULONG + +stdENDP _READ_PORT_ULONG + + + +;++ +; +; VOID +; READ_PORT_BUFFER_UCHAR( +; PUCHAR Port, +; PUCHAR Buffer, +; ULONG Count +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Buffer address +; (esp+12) = Count +; +;-- +cPublicProc _READ_PORT_BUFFER_UCHAR ,3 +cPublicFpo 3, 0 + + mov eax, edi ; Save edi + + mov edx,[esp+4] ; (dx) = Port + mov edi,[esp+8] ; (edi) = buffer + mov ecx,[esp+12] ; (ecx) = transfer count + rep insb + mov edi, eax + stdRET _READ_PORT_BUFFER_UCHAR + +stdENDP _READ_PORT_BUFFER_UCHAR + + +;++ +; +; VOID +; READ_PORT_BUFFER_USHORT( +; PUSHORT Port, +; PUSHORT Buffer, +; ULONG Count +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Buffer address +; (esp+12) = Count +; +;-- +cPublicProc _READ_PORT_BUFFER_USHORT ,3 +cPublicFpo 3, 0 + + mov eax, edi ; Save edi + + mov edx,[esp+4] ; (dx) = Port + mov edi,[esp+8] ; (edi) = buffer + mov ecx,[esp+12] ; (ecx) = transfer count + rep insw + mov edi, eax + stdRET _READ_PORT_BUFFER_USHORT + +stdENDP _READ_PORT_BUFFER_USHORT + + +;++ +; +; VOID +; READ_PORT_BUFFER_ULONG( +; PULONG Port, +; PULONG Buffer, +; ULONG Count +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Buffer address +; (esp+12) = Count +; +;-- +cPublicProc _READ_PORT_BUFFER_ULONG ,3 +cPublicFpo 3, 0 + + mov eax, edi ; Save edi + + mov edx,[esp+4] ; (dx) = Port + mov edi,[esp+8] ; (edi) = buffer + mov ecx,[esp+12] ; (ecx) = transfer count + rep insd + mov edi, eax + stdRET _READ_PORT_BUFFER_ULONG + +stdENDP _READ_PORT_BUFFER_ULONG + + + +;++ +; +; VOID +; WRITE_PORT_UCHAR( +; PUCHAR Port, +; UCHAR Value +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Value +; +;-- +cPublicProc _WRITE_PORT_UCHAR ,2 +cPublicFpo 2, 0 + + mov edx,[esp+4] ; (dx) = Port + mov al,[esp+8] ; (al) = Value + out dx,al + stdRET _WRITE_PORT_UCHAR + +stdENDP _WRITE_PORT_UCHAR + + + +;++ +; +; VOID +; WRITE_PORT_USHORT( +; PUSHORT Port, +; USHORT Value +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Value +; +;-- +cPublicProc _WRITE_PORT_USHORT ,2 +cPublicFpo 2, 0 + + mov edx,[esp+4] ; (dx) = Port + mov eax,[esp+8] ; (ax) = Value + out dx,ax + stdRET _WRITE_PORT_USHORT + +stdENDP _WRITE_PORT_USHORT + + + +;++ +; +; VOID +; WRITE_PORT_ULONG( +; PULONG Port, +; ULONG Value +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Value +; +;-- +cPublicProc _WRITE_PORT_ULONG ,2 +cPublicFpo 2, 0 + + mov edx,[esp+4] ; (dx) = Port + mov eax,[esp+8] ; (eax) = Value + out dx,eax + stdRET _WRITE_PORT_ULONG + +stdENDP _WRITE_PORT_ULONG + + + +;++ +; +; VOID +; WRITE_PORT_BUFFER_UCHAR( +; PUCHAR Port, +; PUCHAR Buffer, +; ULONG Count +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Buffer address +; (esp+12) = Count +; +;-- +cPublicProc _WRITE_PORT_BUFFER_UCHAR ,3 +cPublicFpo 3, 0 + + mov eax,esi ; Save esi + mov edx,[esp+4] ; (dx) = Port + mov esi,[esp+8] ; (esi) = buffer + mov ecx,[esp+12] ; (ecx) = transfer count + rep outsb + mov esi,eax + stdRET _WRITE_PORT_BUFFER_UCHAR + +stdENDP _WRITE_PORT_BUFFER_UCHAR + + +;++ +; +; VOID +; WRITE_PORT_BUFFER_USHORT( +; PUSHORT Port, +; PUSHORT Buffer, +; ULONG Count +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Buffer address +; (esp+12) = Count +; +;-- +cPublicProc _WRITE_PORT_BUFFER_USHORT ,3 +cPublicFpo 3, 0 + + mov eax,esi ; Save esi + mov edx,[esp+4] ; (dx) = Port + mov esi,[esp+8] ; (esi) = buffer + mov ecx,[esp+12] ; (ecx) = transfer count + rep outsw + mov esi,eax + stdRET _WRITE_PORT_BUFFER_USHORT + +stdENDP _WRITE_PORT_BUFFER_USHORT + + +;++ +; +; VOID +; WRITE_PORT_BUFFER_ULONG( +; PULONG Port, +; PULONG Buffer, +; ULONG Count +; ) +; +; Arguments: +; (esp+4) = Port +; (esp+8) = Buffer address +; (esp+12) = Count +; +;-- +cPublicProc _WRITE_PORT_BUFFER_ULONG ,3 +cPublicFpo 3, 0 + + mov eax,esi ; Save esi + mov edx,[esp+4] ; (dx) = Port + mov esi,[esp+8] ; (esi) = buffer + mov ecx,[esp+12] ; (ecx) = transfer count + rep outsd + mov esi,eax + stdRET _WRITE_PORT_BUFFER_ULONG + +stdENDP _WRITE_PORT_BUFFER_ULONG + + +_TEXT$00 ends + + end diff --git a/private/ntos/nthals/halx86/i386/xxkdsup.c b/private/ntos/nthals/halx86/i386/xxkdsup.c new file mode 100644 index 000000000..62ee9da82 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/xxkdsup.c @@ -0,0 +1,404 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + xxkdsup.c + +Abstract: + + Com support. Code to init a com port, store port state, map + portable procedures to x86 procedures. + +Author: + + Bryan M. Willman (bryanwi) 24-Sep-90 + +Revision History: + + Shielin Tzong (shielint) 10-Apr-91 + Add packet control protocol. + + John Vert (jvert) 11-Jul-1991 + Moved from KD/i386 to HAL + +--*/ + +#include "halp.h" +#include "ixkdcom.h" +#include "stdio.h" + +// +// This MUST be initialized to zero so we know not to do anything when +// CpGetByte is called when the kernel debugger is disabled. +// + +CPPORT Port = {NULL, 0, PORT_DEFAULTRATE }; + +// +// Remember the debugger port information +// + +CPPORT PortInformation = {NULL, 0, PORT_DEFAULTRATE}; +ULONG ComPort = 0; + +// +// We need this so the serial driver knows that the kernel debugger +// is using a particular port. The serial driver then knows not to +// touch this port. KdInitCom fills this in with the number of the +// COM port it is using (1 or 2) +// +// This will go in the registry as soon as the registry is working. +// +PUCHAR KdComPortInUse=NULL; + + +BOOLEAN +KdPortInitialize( + PDEBUG_PARAMETERS DebugParameters, + PLOADER_PARAMETER_BLOCK LoaderBlock, + BOOLEAN Initialize + ) + +/*++ + +Routine Description: + + This procedure checks for which COM port should be used by kernel + debugger. If DebugParameter specifies a COM port, we will use it + even if we can not find it (we trust user). Otherwise, if COM2 + is present and there is no mouse attaching to it, we use COM2. + If COM2 is not availabe, we check COM1. If both COM1 and COM2 are + not present, we give up and return false. + +Arguments: + + DebugParameters - Supplies a pointer a structure which optionally + sepcified the debugging port information. + + LoaderBlock - supplies a pointer to the loader parameter block. + + Initialize - Specifies a boolean value that determines whether the + debug port is initialized or just the debug port parameters + are captured. + +Returned Value: + + TRUE - If a debug port is found. + +--*/ + +{ + + PCONFIGURATION_COMPONENT_DATA ConfigurationEntry, ChildEntry; + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PCM_PARTIAL_RESOURCE_LIST List; + ULONG MatchKey, i; + ULONG BaudRate = BD_19200; + PUCHAR PortAddress = NULL; + ULONG Com = 0; + UCHAR DebugMessage[80]; + + // + // Check if Port and baudrate have been determined. + // + + if (PortInformation.Address == NULL) { + + // + // First see if the DebugParameters contains debugging port info. + // + + if (DebugParameters->BaudRate != 0) { + BaudRate = DebugParameters->BaudRate; + Port.Flags &= ~PORT_DEFAULTRATE; + } + + if (DebugParameters->CommunicationPort != 0) { + + // + // Find the configuration information of the specified serial port. + // + + Com = DebugParameters->CommunicationPort; + MatchKey = Com - 1; + if (LoaderBlock != NULL) { + ConfigurationEntry = KeFindConfigurationEntry(LoaderBlock->ConfigurationRoot, + ControllerClass, + SerialController, + &MatchKey); + + } else { + ConfigurationEntry = NULL; + } + + } else { + + // + // Check if COM2 is present and make sure no mouse attaches to it. + // + + MatchKey = 1; + if (LoaderBlock != NULL) { + ConfigurationEntry = KeFindConfigurationEntry(LoaderBlock->ConfigurationRoot, + ControllerClass, + SerialController, + &MatchKey); + + } else { + ConfigurationEntry = NULL; + } + + if (ConfigurationEntry != NULL) { + ChildEntry = ConfigurationEntry->Child; + if ((ChildEntry != NULL) && + (ChildEntry->ComponentEntry.Type == PointerPeripheral)) { + ConfigurationEntry = NULL; + } + } + + // + // If COM2 does not exist or a serial mouse attaches to it, try + // COM1. If COM1 exists, we will use it no matter what is on + // it. + // + + if (ConfigurationEntry == NULL) { + MatchKey = 0; + if (LoaderBlock != NULL) { + ConfigurationEntry = KeFindConfigurationEntry(LoaderBlock->ConfigurationRoot, + ControllerClass, + SerialController, + &MatchKey); + + } else { + ConfigurationEntry = NULL; + } + + if (ConfigurationEntry != NULL) { + Com = 1; + } else if (CpDoesPortExist((PUCHAR)COM2_PORT)) { + PortAddress = (PUCHAR)COM2_PORT; + Com = 2; + } else if (CpDoesPortExist((PUCHAR)COM1_PORT)) { + PortAddress = (PUCHAR)COM1_PORT; + Com = 1; + } else { + return(FALSE); + } + } else { + Com = 2; + } + } + + // + // Get Comport address from the component configuration data. + // (If we find the ComponentEntry associated with the com port) + // + + if (ConfigurationEntry) { + List = (PCM_PARTIAL_RESOURCE_LIST)ConfigurationEntry->ConfigurationData; + for (i = 0; i < List->Count ; i++ ) { + Descriptor = &List->PartialDescriptors[i]; + if (Descriptor->Type == CmResourceTypePort) { + PortAddress = (PUCHAR)Descriptor->u.Port.Start.LowPart; + } + } + } + + // + // If we can not find the port address for the comport, simply use + // default value. + // + + if (PortAddress == NULL) { + switch (Com) { + case 1: + PortAddress = (PUCHAR)0x3f8; + break; + case 2: + PortAddress = (PUCHAR)0x2f8; + break; + case 3: + PortAddress = (PUCHAR)0x3e8; + break; + case 4: + PortAddress = (PUCHAR)0x2e8; + } + } + + // + // Initialize the port structure. + // + + ComPort = Com; + PortInformation.Address = PortAddress; + PortInformation.Baud = BaudRate; + } + + if (Initialize == TRUE) { + CpInitialize(&Port, + PortInformation.Address, + PortInformation.Baud + ); + KdComPortInUse= PortInformation.Address; + sprintf(DebugMessage, MSG_DEBUG_ENABLE, + ComPort, PortInformation.Address, PortInformation.Baud); + HalDisplayString("\n"); + HalDisplayString(DebugMessage); + } + return(TRUE); +} + +ULONG +KdPortGetByte ( + OUT PUCHAR Input + ) + +/*++ + +Routine Description: + + Fetch a byte from the debug port and return it. + + N.B. It is assumed that the IRQL has been raised to the highest level, and + necessary multiprocessor synchronization has been performed before this + routine is called. + +Arguments: + + Input - Returns the data byte. + +Return Value: + + CP_GET_SUCCESS is returned if a byte is successfully read from the + kernel debugger line. + CP_GET_ERROR is returned if error encountered during reading. + CP_GET_NODATA is returned if timeout. + +--*/ + +{ + return CpGetByte(&Port, Input, TRUE); +} + +ULONG +KdPortPollByte ( + OUT PUCHAR Input + ) + +/*++ + +Routine Description: + + Fetch a byte from the debug port and return it if one is available. + + N.B. It is assumed that the IRQL has been raised to the highest level, and + necessary multiprocessor synchronization has been performed before this + routine is called. + +Arguments: + + Input - Returns the data byte. + +Return Value: + + CP_GET_SUCCESS is returned if a byte is successfully read from the + kernel debugger line. + CP_GET_ERROR is returned if error encountered during reading. + CP_GET_NODATA is returned if timeout. + +--*/ + +{ + return CpGetByte(&Port, Input, FALSE); +} + +VOID +KdPortPutByte ( + IN UCHAR Output + ) + +/*++ + +Routine Description: + + Write a byte to the debug port. + + N.B. It is assumed that the IRQL has been raised to the highest level, and + necessary multiprocessor synchronization has been performed before this + routine is called. + +Arguments: + + Output - Supplies the output data byte. + +Return Value: + + None. + +--*/ + +{ + CpPutByte(&Port, Output); +} + +VOID +KdPortRestore ( + VOID + ) + +/*++ + +Routine Description: + + This routine does NOTHING on the x86. + + N.B. It is assumed that the IRQL has been raised to the highest level, and + necessary multiprocessor synchronization has been performed before this + routine is called. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + Port.Flags &= ~PORT_SAVED; +} + +VOID +KdPortSave ( + VOID + ) + +/*++ + +Routine Description: + + This routine does NOTHING on the x86. + + N.B. It is assumed that the IRQL has been raised to the highest level, and + necessary multiprocessor synchronization has been performed before this + routine is called. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + Port.Flags |= PORT_SAVED; +} + diff --git a/private/ntos/nthals/halx86/i386/xxmemory.c b/private/ntos/nthals/halx86/i386/xxmemory.c new file mode 100644 index 000000000..c88792a85 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/xxmemory.c @@ -0,0 +1,368 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + xxmemory.c + +Abstract: + + Provides routines to allow the HAL to map physical memory. + +Author: + + John Vert (jvert) 3-Sep-1991 + +Environment: + + Phase 0 initialization only. + +Revision History: + +--*/ +#include "halp.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,HalpAllocPhysicalMemory) +#endif + + +MEMORY_ALLOCATION_DESCRIPTOR HalpExtraAllocationDescriptor; + +// +// Almost all of the last 4Mb of memory are available to the HAL to map +// physical memory. The kernel may use a couple of PTEs in this area for +// special purposes, so skip any which are not zero. +// +// Note that the HAL's heap only uses the last 3Mb. This is so we can +// reserve the first 1Mb for use if we have to return to real mode. +// In order to return to real mode we need to identity-map the first 1Mb of +// physical memory. +// +PVOID HalpHeapStart=(PVOID)0xffd00000; + + +PVOID +HalpMapPhysicalMemory( + IN PVOID PhysicalAddress, + IN ULONG NumberPages + ) + +/*++ + +Routine Description: + + This routine maps physical memory into the area of virtual memory + reserved for the HAL. It does this by directly inserting the PTE + into the Page Table which the OS Loader has provided. + + N.B. This routine does *NOT* update the MemoryDescriptorList. The + caller is responsible for either removing the appropriate + physical memory from the list, or creating a new descriptor to + describe it. + +Arguments: + + PhysicalAddress - Supplies the physical address of the start of the + area of physical memory to be mapped. + + NumberPages - Supplies the number of pages contained in the area of + physical memory to be mapped. + +Return Value: + + PVOID - Virtual address at which the requested block of physical memory + was mapped + + NULL - The requested block of physical memory could not be mapped. + +--*/ + +{ + PHARDWARE_PTE PTE; + ULONG PagesMapped; + PVOID VirtualAddress; + + // + // The OS Loader sets up hyperspace for us, so we know that the Page + // Tables are magically mapped starting at V.A. 0xC0000000. + // + + PagesMapped = 0; + while (PagesMapped < NumberPages) { + // + // Look for enough consecutive free ptes to honor mapping + // + + PagesMapped = 0; + VirtualAddress = HalpHeapStart; + + while (PagesMapped < NumberPages) { + PTE=MiGetPteAddress(VirtualAddress); + if (*(PULONG)PTE != 0) { + + // + // Pte is not free, skip up to the next pte and start over + // + + HalpHeapStart = (PVOID) ((ULONG)VirtualAddress + PAGE_SIZE); + break; + } + VirtualAddress = (PVOID) ((ULONG)VirtualAddress + PAGE_SIZE); + PagesMapped++; + } + + } + + + PagesMapped = 0; + VirtualAddress = (PVOID) ((ULONG) HalpHeapStart | BYTE_OFFSET (PhysicalAddress)); + while (PagesMapped < NumberPages) { + PTE=MiGetPteAddress(HalpHeapStart); + + PTE->PageFrameNumber = ((ULONG)PhysicalAddress >> PAGE_SHIFT); + PTE->Valid = 1; + PTE->Write = 1; + + PhysicalAddress = (PVOID)((ULONG)PhysicalAddress + PAGE_SIZE); + HalpHeapStart = (PVOID)((ULONG)HalpHeapStart + PAGE_SIZE); + + ++PagesMapped; + } + + // + // Flush TLB + // + HalpFlushTLB (); + return(VirtualAddress); +} + +PVOID +HalpMapPhysicalMemoryWriteThrough( + IN PVOID PhysicalAddress, + IN ULONG NumberPages +) +/*++ + +Routine Description: + + Maps a physical memory address into virtual space, same as + HalpMapPhysicalMemory(). The difference is that this routine + marks the pages as PCD/PWT so that writes to the memory mapped registers + mapped here won't get delayed in the future x86 internal write-back caches. + +Arguments: + + PhysicalAddress - Supplies a physical address of the memory to be mapped + + NumberPages - Number of pages to map + +Return Value: + + Virtual address pointer to the requested physical address + +--*/ +{ + ULONG Index; + PHARDWARE_PTE PTE; + PVOID VirtualAddress; + + VirtualAddress = HalpMapPhysicalMemory(PhysicalAddress, NumberPages); + PTE = MiGetPteAddress(VirtualAddress); + + for (Index = 0; Index < NumberPages; Index++, PTE++) { + + PTE->CacheDisable = 1; + PTE->WriteThrough = 1; + } + + return VirtualAddress; +} + + +PVOID +HalpRemapVirtualAddress( + IN PVOID VirtualAddress, + IN PVOID PhysicalAddress, + IN BOOLEAN WriteThrough + ) +/*++ + +Routine Description: + + This routine remaps a PTE to the physical memory address provided. + +Arguments: + + PhysicalAddress - Supplies the physical address of the area to be mapped + + VirtualAddress - Valid address to be remapped + + WriteThrough - Map as cachable or WriteThrough + +Return Value: + + PVOID - Virtual address at which the requested block of physical memory + was mapped + + NULL - The requested block of physical memory could not be mapped. + +--*/ +{ + PHARDWARE_PTE PTE; + + PTE = MiGetPteAddress (VirtualAddress); + PTE->PageFrameNumber = ((ULONG)PhysicalAddress >> PAGE_SHIFT); + PTE->Valid = 1; + PTE->Write = 1; + + if (WriteThrough) { + PTE->CacheDisable = 1; + PTE->WriteThrough = 1; + } + + // + // Flush TLB + // + HalpFlushTLB(); + return(VirtualAddress); + +} + +ULONG +HalpAllocPhysicalMemory( + IN PLOADER_PARAMETER_BLOCK LoaderBlock, + IN ULONG MaxPhysicalAddress, + IN ULONG NoPages, + IN BOOLEAN bAlignOn64k + ) +/*++ + +Routine Description: + + Carves out N pages of physical memory from the memory descriptor + list in the desired location. This function is to be called only + during phase zero initialization. (ie, before the kernel's memory + management system is running) + +Arguments: + + MaxPhysicalAddress - The max address where the physical memory can be + NoPages - Number of pages to allocate + +Return Value: + + The physical address or NULL if the memory could not be obtained. + +--*/ +{ + PMEMORY_ALLOCATION_DESCRIPTOR Descriptor; + PLIST_ENTRY NextMd; + ULONG AlignmentOffset; + ULONG MaxPageAddress; + ULONG PhysicalAddress; + + MaxPageAddress = MaxPhysicalAddress >> PAGE_SHIFT; + + // + // Scan the memory allocation descriptors and allocate map buffers + // + + NextMd = LoaderBlock->MemoryDescriptorListHead.Flink; + while (NextMd != &LoaderBlock->MemoryDescriptorListHead) { + Descriptor = CONTAINING_RECORD(NextMd, + MEMORY_ALLOCATION_DESCRIPTOR, + ListEntry); + + AlignmentOffset = bAlignOn64k ? + ((Descriptor->BasePage + 0x0f) & ~0x0f) - Descriptor->BasePage : + 0; + + // + // Search for a block of memory which is contains a memory chuck + // that is greater than size pages, and has a physical address less + // than MAXIMUM_PHYSICAL_ADDRESS. + // + + if ((Descriptor->MemoryType == LoaderFree || + Descriptor->MemoryType == MemoryFirmwareTemporary) && + (Descriptor->BasePage) && + (Descriptor->PageCount >= NoPages + AlignmentOffset) && + (Descriptor->BasePage + NoPages + AlignmentOffset < MaxPageAddress)) { + + PhysicalAddress = + (Descriptor->BasePage + AlignmentOffset) << PAGE_SHIFT; + break; + } + + NextMd = NextMd->Flink; + } + + // + // Use the extra descriptor to define the memory at the end of the + // original block. + // + + + ASSERT(NextMd != &LoaderBlock->MemoryDescriptorListHead); + + if (NextMd == &LoaderBlock->MemoryDescriptorListHead) + return (ULONG)NULL; + + // + // Adjust the memory descriptors. + // + + if (AlignmentOffset == 0) { + + Descriptor->BasePage += NoPages; + Descriptor->PageCount -= NoPages; + + if (Descriptor->PageCount == 0) { + + // + // The whole block was allocated, + // Remove the entry from the list completely. + // + + RemoveEntryList(&Descriptor->ListEntry); + + } + + } else { + + if (Descriptor->PageCount - NoPages - AlignmentOffset) { + + // + // Currently we only allow one Align64K allocation + // + ASSERT (HalpExtraAllocationDescriptor.PageCount == 0); + + // + // The extra descriptor is needed so intialize it and insert + // it in the list. + // + HalpExtraAllocationDescriptor.PageCount = + Descriptor->PageCount - NoPages - AlignmentOffset; + + HalpExtraAllocationDescriptor.BasePage = + Descriptor->BasePage + NoPages + AlignmentOffset; + + HalpExtraAllocationDescriptor.MemoryType = MemoryFree; + InsertTailList( + &Descriptor->ListEntry, + &HalpExtraAllocationDescriptor.ListEntry + ); + } + + + // + // Use the current entry as the descriptor for the first block. + // + + Descriptor->PageCount = AlignmentOffset; + } + + return PhysicalAddress; +} diff --git a/private/ntos/nthals/halx86/i386/xxstubs.c b/private/ntos/nthals/halx86/i386/xxstubs.c new file mode 100644 index 000000000..251bce23c --- /dev/null +++ b/private/ntos/nthals/halx86/i386/xxstubs.c @@ -0,0 +1,131 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + stubs.c + +Abstract: + + This implements the HAL routines which don't do anything on x86. + +Author: + + John Vert (jvert) 11-Jul-1991 + +Revision History: + +--*/ +#include "nthal.h" +#include "arc.h" +#include "arccodes.h" + +VOID +HalSaveState( + VOID + ) + +/*++ + +Routine Description: + + Saves the system state into the restart block. Currently does nothing. + +Arguments: + + None + +Return Value: + + Does not return + +--*/ + +{ + DbgPrint("HalSaveState called - System stopped\n"); + + KeBugCheck(0); +} + + +BOOLEAN +HalDataBusError( + VOID + ) + +/*++ + +Routine Description: + + Called when a data bus error occurs. There is no way to fix this on + x86. + +Arguments: + + None + +Return Value: + + FALSE + +--*/ + +{ + return(FALSE); + +} + +BOOLEAN +HalInstructionBusError( + VOID + ) + +/*++ + +Routine Description: + + Called when an instruction bus error occurs. There is no way to fix this + on x86. + +Arguments: + + None + +Return Value: + + FALSE + +--*/ + +{ + return(FALSE); + +} + +VOID +KeFlushWriteBuffer( + VOID + ) + +/*++ + +Routine Description: + + Flushes all write buffers and/or other data storing or reordering + hardware on the current processor. This ensures that all previous + writes will occur before any new reads or writes are completed. + +Arguments: + + None + +Return Value: + + None. + +--*/ + +{ + +} diff --git a/private/ntos/nthals/halx86/i386/xxtime.c b/private/ntos/nthals/halx86/i386/xxtime.c new file mode 100644 index 000000000..f726cc184 --- /dev/null +++ b/private/ntos/nthals/halx86/i386/xxtime.c @@ -0,0 +1,91 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + xxtime.c + +Abstract: + + This module implements the HAL set/query realtime clock routines for + an x86 system. + +Author: + + David N. Cutler (davec) 5-May-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "halp.h" + +BOOLEAN +HalQueryRealTimeClock ( + OUT PTIME_FIELDS TimeFields + ) + +/*++ + +Routine Description: + + This routine queries the realtime clock. + + N.B. This routine assumes that the caller has provided any required + synchronization to query the realtime clock information. + +Arguments: + + TimeFields - Supplies a pointer to a time structure that receives + the realtime clock information. + +Return Value: + + If the power to the realtime clock has not failed, then the time + values are read from the realtime clock and a value of TRUE is + returned. Otherwise, a value of FALSE is returned. + +--*/ + +{ + + HalpReadCmosTime(TimeFields); + return TRUE; +} + +BOOLEAN +HalSetRealTimeClock ( + IN PTIME_FIELDS TimeFields + ) + +/*++ + +Routine Description: + + This routine sets the realtime clock. + + N.B. This routine assumes that the caller has provided any required + synchronization to set the realtime clock information. + +Arguments: + + TimeFields - Supplies a pointer to a time structure that specifies the + realtime clock information. + +Return Value: + + If the power to the realtime clock has not failed, then the time + values are written to the realtime clock and a value of TRUE is + returned. Otherwise, a value of FALSE is returned. + +--*/ + +{ + HalpWriteCmosTime(TimeFields); + return TRUE; +} -- cgit v1.2.3