diff options
Diffstat (limited to '')
81 files changed, 20589 insertions, 0 deletions
diff --git a/private/ntos/nthals/halcbus/bushnd.c b/private/ntos/nthals/halcbus/bushnd.c new file mode 100644 index 000000000..a1e648dc1 --- /dev/null +++ b/private/ntos/nthals/halcbus/bushnd.c @@ -0,0 +1,7 @@ +// +// This file simply includes the common sources from the current HAL +// directory. When the structure is finally changed, the real file should +// be in this directory. +// + +#include "..\bushnd.c" diff --git a/private/ntos/nthals/halcbus/dirs b/private/ntos/nthals/halcbus/dirs new file mode 100644 index 000000000..89282b95a --- /dev/null +++ b/private/ntos/nthals/halcbus/dirs @@ -0,0 +1,24 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=eisa mca + +OPTIONAL_DIRS= diff --git a/private/ntos/nthals/halcbus/drivesup.c b/private/ntos/nthals/halcbus/drivesup.c new file mode 100644 index 000000000..38259e5f4 --- /dev/null +++ b/private/ntos/nthals/halcbus/drivesup.c @@ -0,0 +1,7 @@ +// +// This file simply includes the common sources from the current HAL +// directory. When the structure is finally changed, the real file should +// be in this directory. +// + +#include "..\drivesup.c" diff --git a/private/ntos/nthals/halcbus/eisa/makefile b/private/ntos/nthals/halcbus/eisa/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nthals/halcbus/eisa/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nthals/halcbus/eisa/makefile.inc b/private/ntos/nthals/halcbus/eisa/makefile.inc new file mode 100644 index 000000000..e2bb55a56 --- /dev/null +++ b/private/ntos/nthals/halcbus/eisa/makefile.inc @@ -0,0 +1,2 @@ +obj\i386\hal.def: ..\..\hal.src + $(TARGET_CPP) /EP -Di386 $(C_DEFINES) ..\..\hal.src > obj\i386\hal.def diff --git a/private/ntos/nthals/halcbus/eisa/sources b/private/ntos/nthals/halcbus/eisa/sources new file mode 100644 index 000000000..a79192498 --- /dev/null +++ b/private/ntos/nthals/halcbus/eisa/sources @@ -0,0 +1,112 @@ + +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=hal + +TARGETNAME=halcbus +TARGETPATH=$(BASEDIR)\public\sdk\lib + +!IF $(386) + +TARGETTYPE=HAL +NT_UP=0 + +!ELSE + +TARGETTYPE=DRIVER + +!ENDIF + +INCLUDES=..\..\..\inc;..\..\..\ke;..\..\;..;..\..\halx86\i386 + +SOURCES= + +i386_SOURCES=hal.rc \ + drivesup.c \ + bushnd.c \ + rangesup.c \ + ..\i386\ixbeep.asm \ + ..\i386\ixbusdat.c \ + ..\i386\ixdat.c \ + ..\i386\ixinfo.c \ + ..\i386\ixisabus.c \ + ..\i386\ixpcibus.c \ + ..\i386\ixpciint.c \ + ..\i386\ixpcibrd.c \ + ..\i386\ixcmos.asm \ + ..\i386\ixenvirv.c \ + ..\i386\ixfirm.c \ + ..\i386\ixhwsup.c \ + ..\i386\ixidle.asm \ + ..\i386\ixisasup.c \ + ..\i386\ixkdcom.c \ + ..\i386\ixphwsup.c \ + ..\i386\ixprofil.asm \ + ..\i386\ixreboot.c \ + ..\i386\ixthunk.c \ + ..\i386\ixusage.c \ + ..\i386\xxbiosa.asm \ + ..\i386\xxbiosc.c \ + ..\i386\xxdisp.c \ + ..\i386\xxkdsup.c \ + ..\i386\xxmemory.c \ + ..\i386\xxstubs.c \ + ..\i386\xxtime.c \ + ..\i386\cbapic.c \ + ..\i386\cbioacc.asm \ + ..\i386\cb1stall.asm \ + ..\i386\cb2stall.asm \ + ..\i386\cbswint.asm \ + ..\i386\cbsysint.asm \ + ..\i386\cbmapint.c \ + ..\i386\cbdriver.c \ + ..\i386\cbusnmi.c \ + ..\i386\cbusboot.asm \ + ..\i386\cbuslock.asm \ + ..\i386\cbusprof.asm \ + ..\i386\cbusmisc.asm \ + ..\i386\cbus2cbc.asm \ + ..\i386\cbusapic.asm \ + ..\i386\cbus2.c \ + ..\i386\cbus1.c \ + ..\i386\cbus1bt.asm \ + ..\i386\cbus2ecc.c \ + ..\i386\cbus.c \ + ..\i386\cbus_sw.c \ + ..\i386\cbusmem.c \ + ..\i386\cbushal.c \ + ..\i386\cbusproc.c + + +DLLDEF=obj\*\hal.def + +MSC_WARNING_LEVEL=/W3 /WX + +!IF $(386) + +NTTARGETFILES=$(TARGETPATH)\i386\halcbus.lib + +!ENDIF diff --git a/private/ntos/nthals/halcbus/hal.rc b/private/ntos/nthals/halcbus/hal.rc new file mode 100644 index 000000000..3cba4ad89 --- /dev/null +++ b/private/ntos/nthals/halcbus/hal.rc @@ -0,0 +1,11 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Hardware Abstraction Layer DLL" +#define VER_INTERNALNAME_STR "hal.dll" + +#include "common.ver" + diff --git a/private/ntos/nthals/halcbus/i386/cb1stall.asm b/private/ntos/nthals/halcbus/i386/cb1stall.asm new file mode 100644 index 000000000..719627aef --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cb1stall.asm @@ -0,0 +1,932 @@ + title "Initialize Stall Execution for the Corollary MP machines" +;++ +; +;Copyright (c) 1992, 1993, 1994 Corollary Inc +; +;Module Name: +; +; cb1stall.asm +; +;Abstract: +; +; This module contains various stall initialization, clock and performance +; counter routines. +; +;Author: +; +; Landy Wang (landy@corollary.com) 05-Oct-1992 +; +;Revision History: +; +;-- + + + +.386p + .xlist +include hal386.inc +include i386\kimacro.inc +include callconv.inc ; calling convention macros +include cbus.inc +include mac386.inc +include i386\ix8259.inc + + EXTRNP _HalBeginSystemInterrupt,3 + EXTRNP _HalEndSystemInterrupt,2 + + .list + +; +; the CLKIN pin of the 82489DX is clocking at 32MHz (32000000) on the bridge, +; and 33-1/3 (333333333) MHz on the additional processor cards. +; +; divide this by 1,000,000 to get clocking in a microsecond --> ~33. +; 33 * 16 microseconds == 528 == 0x210 +; + +CLKIN_ONE_SECOND EQU 32000000 ; in APIC CLKIN units +; CLKIN_ONE_SECOND EQU 33333333 ; in APIC CLKIN units +CLKIN_SIXTEEN_MS EQU 210h ; 16 microseconds (CLKIN) +CLKIN_SIXTEEN_MS_SHIFT EQU 4 ; shift 16 microseconds to 1ms +CLKIN_ENABLE_ONESHOT EQU 0h ; enable one-shot CLKIN interrupt +CLKIN_ENABLE_PERIODIC EQU 20000h ; enable periodic CLKIN interrupts +CLKIN_DISABLE EQU 10000h ; mask off CLKIN interrupts + +APIC_TIMER_MILLISECOND EQU 32000 ; timer units to == 1 millisecond +APIC_TIMER_MICROSECOND EQU 33 ; timer units to == 1 microsecond + +TIMER_VECTOR_ENTRY EQU 320h ; timer vector table entry 0 address +INITIAL_COUNT_REG EQU 380h ; poke here to set the initial count +CURRENT_COUNT_REG EQU 390h ; current counter countdown is here + +D_INT032 EQU 8E00h ; access word for 386 ring 0 int gate + +CBUS1_PERF_TASKPRI EQU 0DDh ; vector generated by 8254 timer + +; +; The default interval between clock interrupts is +; 10 milliseconds == 10000 microseconds == 100000 (in 100 ns units). +; Remember the NT executive expects this value to be in +; 100 nanosecond units (not microseconds), so multiply +; accordingly when referencing KeTimeIncrement. +; +; the maxclock rates below are not the slowest that the hardware can support - +; they are the slowest we want NT to restore it whenever it is rolled back. +; +MAXCLOCK_RATE_IN_MS EQU 10 ; specify in milliseconds + +; +; We will support clock interrupt generation with a minimum of 300 +; nanosecond separations to as much as 10 milliseconds apart. +; +MINCLOCK_DELTA_IN_100NS EQU 3 ; specify in 100-nanosecond units +MAXCLOCK_DELTA_IN_100NS EQU 100000 ; specify in 100-nanosecond units + +; +; "Convert" the interval to rollover count for 8254 Timer1 device. +; timer1 counts down a 16 bit value at a rate of 1.193M counts-per-sec. +; So that's what we'll use to get the finest granularity. Note that +; this is solely for the performance counter and has NOTHING to do +; with the system clock, which is driven directly from the APIC. +; We'd like for the interrupt rate to be even lower (ie: once per +; second rather than approximately 17 times per second, but the 8254 +; only gives us two bytes to feed in the interrupt counter. +; + +ROLLOVER_COUNT EQU 0ffffH ; must fit in the 8254's two bytes +PERFORMANCE_FREQUENCY EQU 1193000 + +TIMER1_DATA_PORT0 EQU 40H ; Timer1, channel 0 data port +TIMER1_CONTROL_PORT0 EQU 43H ; Timer1, channel 0 control port + +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 + +_DATA SEGMENT DWORD PUBLIC 'DATA' +; +; default clock rate is 10 milliseconds +; +CbusClockRateInMillis dd 10 ; in milliseconds + +Cbus1PerfCounterInit dd 0 +Cbus1PerfCounterLow dd 0 +Cbus1PerfCounterHigh dd 0 +Cbus1LastReadPerfLow dd 0 +Cbus1LastReadPerfHigh dd 0 +Cbus18254Late dd 0 +Cbus1CurrentTimeIncrement dd 0 + +if DBG +Cbus18254LateCount dd 0 +Cbus1Queries dd 0 +endif + +_DATA ends + + +INIT SEGMENT PARA PUBLIC 'CODE' ; Start 32 bit code + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "Initialize Stall Execution Counter" +;++ +; +; VOID +; Cbus1InitializeStall ( +; IN CCHAR ProcessorNumber +; ) +; +; Routine Description: +; +; This routine initializes the per Microsecond counter for +; KeStallExecutionProcessor. +; +; All Corollary processors have their own local APIC and +; their own local timers. each processor independently +; calibrates himself here, thus supporting processors of +; varying speeds. +; +; Arguments: +; +; ProcessorNumber - Processor Number +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _Cbus1InitializeStall ,1 +cPublicFpo 1,2 + + push ebp ; save ebp + mov ebp, esp + sub esp, 8 ; save room for idtr + + pushfd ; save caller's eflag + + cli ; disable interrupts + + ; + ; save the current CbusClockVector IDT entry, as we are + ; going to temporarily repoint it at a private handler. + ; + + sidt fword ptr [ebp-8] ; get IDTR (base & limit) + mov ecx, [ebp-6] ; get IDTR base value + + mov eax, [_CbusClockVector] + + 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 + push ecx ; (TOS) -> &IDT[CbusClockVector] + + mov eax, offset FLAT:TimerExpired + + 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 eax, [_CbusClockVector] ; we expect this vector to... + or eax, CLKIN_ENABLE_ONESHOT ; use one-shot CLKIN to interrupt us + + ; get the base of APIC space, so we can then access + ; the addr of hardware interrupt command register below + + mov ecx, [_CbusLocalApic] + + ; the count register appears to decrement approx 0x30 per + ; asm instruction on a 486/33, just fyi. so load it up with + ; a "real high value" so we don't get an interrupt whilst setting + ; up the local time vector entry, etc, and then at the last + ; possible moment, fill it in with the desired starting value. + + mov dword ptr INITIAL_COUNT_REG[ecx], CLKIN_ONE_SECOND + + ; initialize the local timer vector table entry with + ; appropriate Vector, Timer Base, Timer Mode and Mask. + + mov TIMER_VECTOR_ENTRY[ecx], eax + + ; poke initial count reg to interrupt us in 16 microseconds + + mov dword ptr INITIAL_COUNT_REG[ecx], CLKIN_SIXTEEN_MS + + xor eax, eax ; initialize our register counter +ALIGN 16 + sti ; enable the interrupt + jmp kise10 + +ALIGN 16 +kise10: + sub eax, 1 ; increment the loopcount + jnz short kise10 + +if DBG + stdCall _DbgBreakPoint ; Counter overflowed! +endif + jmp short kise10 + +TimerExpired: + + ; take the timer expiration interrupt here + +if DBG + cmp eax, 0 + jnz short kise30 + stdCall _DbgBreakPoint ; Counter was never bumped! + ; never return +kise30: +endif + + neg eax + shr eax, CLKIN_SIXTEEN_MS_SHIFT ; convert to microseconds + + mov dword ptr PCR[PcStallScaleFactor], eax + + mov eax, [_CbusLocalApic] + + ; mask off local timer vector table entry now that we're done with it. + + mov dword ptr TIMER_VECTOR_ENTRY[eax], CLKIN_DISABLE + + ; + ; Dismiss the interrupt AFTER disabling the timer entry so + ; we don't get an extra interrupt later that was already pending. + ; + + mov eax, _CbusClockVector ; mark interrupting vector + CBUS_EOI eax, ecx ; destroy eax & ecx + + add esp, 12 ; unload flags, cs, ip + + pop ecx ; (ecx) -> &IDT[CbusClockVector] + pop [ecx+4] ; restore higher half of RTC desc + pop [ecx] ; restore lower half of RTC desc + + popfd ; restore caller's eflags + mov esp, ebp + pop ebp ; restore ebp + + stdRET _Cbus1InitializeStall + +stdENDP _Cbus1InitializeStall + + page ,132 + subttl "Cbus1 Initialize Performance Counter" +;++ +; +; VOID +; Cbus1InitializePerf ( +; ) +; +; Routine Description: +; +; Initialize the 8254 to interrupt the minimum number of times +; per second on the boot processor only to support the performance counter. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- +cPublicProc _Cbus1InitializePerf ,0 + + ; + ; Since ke\i386\allproc.c no longer boots all the available + ; processors in the machine, the first processor to boot must + ; initialize the 8254 and take the extra 8254 ticks. + ; + + mov eax, PCR[PcHal.PcrNumber] + cmp eax, 0 + jne short @f + + pushfd ; save caller's eflag + cli ; make sure interrupts are disabled + + ; + ; Initialize the APIC so that 8254 interrupts go only to the boot + ; processor - there is no need for all of them to get this interrupt + ; when all it is doing is updating a global counter. + ; + + ; stdCall _HalEnableSystemInterrupt,<CBUS1_PERF_TASKPRI,CLOCK2_LEVEL,0> + ; no need to enable - it's done by our caller + + ; + ; Set clock rate at the 8254 + ; + + 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 ecx, ROLLOVER_COUNT + 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 Cbus1PerfCounterInit, 1 + + align 4 +@@: + + stdRET _Cbus1InitializePerf + +stdENDP _Cbus1InitializePerf + + INIT ends + +_TEXT SEGMENT DWORD PUBLIC 'CODE' ; Start 32 bit code + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "Cbus1 Initialize Clock" +;++ +; +; VOID +; Cbus1InitializeClock ( +; ) +; +; Routine Description: +; +; Initializes the clock and the kernel variable for the amount of +; time between timer ticks. The kernel needs this information by the end +; of phase 0. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _Cbus1InitializeClock,0 + + ; + ; Fill in value with the time between clock ticks, + ; remember the NT executive expects this value to be in + ; 100 nanosecond units, not microseconds, so multiply accordingly. + ; + + mov eax, CbusClockRateInMillis ; current rate in milliseconds + + mov ecx, 10000 ; converting back to 100 ns + mul ecx ; eax == 100ns unit value + + ; + ; The Cbus1CurrentTimeIncrement value is used by the clock + ; interrupt routine to pass to the kernel, so set it now. + ; + mov Cbus1CurrentTimeIncrement, eax + + stdCall _Cbus1ProgramClock + + stdCall _KeSetTimeIncrement, <MAXCLOCK_DELTA_IN_100NS, MINCLOCK_DELTA_IN_100NS> + + stdRET _Cbus1InitializeClock + +stdENDP _Cbus1InitializeClock + +;++ +; +; VOID +; Cbus1ProgramClock ( +; ) +; +; Routine Description: +; +; This routine initializes the system time clock for each calling +; processor to generate periodic interrupts at the specified rate using +; this processor's local APIC timer. Thus, each processor must call +; this routine to set or change his clock rate. During startup, each +; processor will call this routine to set his own clock rate. Later, +; when the system is running, the HalSetTimerResolution API is only +; supposed to change the clock rate for the boot processor - this is +; for multimedia apps that want timeouts serviced at a better than +; 5 millisecond granularity. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _Cbus1ProgramClock ,0 + + mov ecx, CbusClockRateInMillis ; new rate in milliseconds + + mov eax, APIC_TIMER_MILLISECOND ; counter per microsecond + + mul ecx ; eax == new APIC countdown val + + mov edx, [_CbusClockVector] ; we expect this vector to... + or edx, CLKIN_ENABLE_PERIODIC ; use CLKIN to interrupt us + + mov ecx, [_CbusLocalApic] + + pushfd + cli + + ; as a frame of reference, the count register decrements + ; approximately 0x30 per asm instruction on a 486/33. + + ; initialize the local timer vector table entry with + ; appropriate Vector, Timer Base, Timer Mode and Mask. + + mov TIMER_VECTOR_ENTRY[ecx], edx + + ; poke initial count reg to start the periodic clock timer interrupts. + ; the IDT entry is valid & enabled on entry to this routine. + + mov dword ptr INITIAL_COUNT_REG[ecx], eax + + mov eax, CbusClockRateInMillis ; new rate in milliseconds + mov ecx, 10000 ; converting back to 100 ns + xor edx, edx + mul ecx ; eax == 100ns unit value + + ; + ; store it here so the clock ISR can tell it to the kernel + ; + mov Cbus1CurrentTimeIncrement, eax + + popfd + stdRET _Cbus1ProgramClock + +stdENDP _Cbus1ProgramClock + + page ,132 + subttl "Query Performance Counter" +;++ +; +; LARGE_INTEGER +; Cbus1QueryPerformanceCounter ( +; OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL +; ) +; +; Routine Description: +; +; This routine returns the current 64-bit performance counter. +; The Performance Frequency is also returned if asked for. +; +; Also note that the performance counter returned by this routine +; is not necessarily the value exactly when this routine was entered. +; The value returned is actually the counter value at any point +; between the routine's entrance and exit times. +; +; This routine is not susceptible to the 2 problems that plague most +; multiprocessor HALs. Their problems are as follows: +; +; a) If the boot processor (or whoever updates the global performance +; counters) misses an interrupt, the counter rolls over undetected, +; and the performance counter returned can actually roll backwards! +; +; b) If you are on an additional processor and for some reason, +; the boot processor is holding off a pending clock interrupt for +; the entire time the additional processor is executing in +; KeQueryPerfomanceCounter. The boot processor hasn't necessarily +; lost a clock interrupt, it's just that he hasn't dropped IRQL +; enough to get it yet, and there isn't any good way for the +; additional processor to force him to. Since the boot processor +; is the only one maintaining the PerfCounter[High,Low], this can +; result in processors returning potentially non-uniform snapshots, +; which can even roll backwards! +; +; Both of these problems have been solved in the Corollary HAL by +; using separate clocks for the system timer and the performance counter. +; Ie: the APIC timer is used for the system timer and interrupts each +; processor every 15 milliseconds. The 8254 interrupts ONLY the boot +; processor once every second. Thus, case a) above cannot happen +; unless the boot processor disables interrupts for more than one second. +; If this happens, the entire system will be badly broken. case b) +; is also bypassed by using a global lock in the performance counter +; interrupt handler. Since the interrupt only occurs once per second +; and only on one processor, the global lock will not hinder performance. +; This allows us to take globally synchronized performance counter +; snapshots and also detect in software if case b) is happening, and +; handle it in software. Because the 8254 timer register is only 16 bits +; wide, the actual 8254 synchronization interrupt will occur approximately +; seventeen times per second instead of once. This is still at least 4 +; times better than the system timer (approximately 70 times per second). +; +; 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+4] ; User supplied Performance Frequency + +cPublicProc _Cbus1QueryPerformanceCounter ,1 + + ; + ; First check to see if the performance counter has been + ; initialized yet. Since the kernel debugger calls + ; KeQueryPerformanceCounter to support the !timer command + ; _VERY_ early on, we need to return something reasonable + ; even though timer initialization hasn't occured yet. + ; + cmp Cbus1PerfCounterInit, 0 + jne short @f ; ok, perf counter has been initialized + + ; + ; Initialization hasn't occured yet, so just return zeros. + ; + mov eax, 0 + mov edx, 0 + jmp retfreq ; retfreq too far away for short jmp + + align 4 +@@: +if DBG + inc dword ptr [Cbus1Queries] +endif + push ebx + push esi + ; + ; all interrupts must be disabled prior to lock acquisition to + ; prevent deadlock. + ; + pushfd + cli + + lea esi, _Halp8254Lock + + align 4 +Kqpc00: + ACQUIRE_SPINLOCK esi, Kqpc01 + + ; + ; get the global timer counters which are incremented + ; one processor only. + ; + + mov esi, Cbus1PerfCounterLow + mov ebx, Cbus1PerfCounterHigh ; [ebx:esi] = 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. + + neg ecx ; PIT counts down to 0h + add ecx, ROLLOVER_COUNT + add esi, ecx + adc ebx, 0 ; [ebx:esi] = Final result + + ; + ; Check whether case b) could be happening right now - this is + ; possible no matter which processor is currently calling us. + ; + + cmp ebx, dword ptr [Cbus1LastReadPerfHigh] + jl short caseb + + cmp esi, dword ptr [Cbus1LastReadPerfLow] + jl short caseb + + jmp short notpending + + align 4 +caseb: + ; + ; Detected case b) happening RIGHT NOW! + ; Update the Cbus1 performance counter NOW, and + ; set a flag so the interrupt handler will NOT. + ; This case actually happens fairly frequently, + ; ie: at least every few seconds on an idle + ; uniprocessor, so this code is quite useful. + ; + + add Cbus1PerfCounterLow, ROLLOVER_COUNT ; update performance counter + adc Cbus1PerfCounterHigh, 0 + inc dword ptr [Cbus18254Late] + +if DBG + inc dword ptr [Cbus18254LateCount] +endif + + align 4 +notpending: + ; + ; save last performance counter read so future callers can compare + ; + + mov Cbus1LastReadPerfLow, esi + mov Cbus1LastReadPerfHigh, ebx + + mov edx, ebx ; save return value + mov eax, esi ; save return value + + lea esi, _Halp8254Lock + RELEASE_SPINLOCK esi + + popfd + pop esi + pop ebx + + ; + ; if asked to, return the frequency in units/second + ; + + align 4 +retfreq: + + or dword ptr KqpcFrequency, 0 ; is it NULL? + jz short @f ; if z, yes, go exit + + mov ecx, KqpcFrequency ; frequency pointer + mov dword ptr [ecx], PERFORMANCE_FREQUENCY ; set frequency + mov dword ptr [ecx+4], 0 ; currently < 4Gig! + + align 4 +@@: + stdRET _Cbus1QueryPerformanceCounter + + align 4 +Kqpc01: + SPIN_ON_SPINLOCK esi,<Kqpc00> + +stdENDP _Cbus1QueryPerformanceCounter + + page ,132 + subttl "Cbus1 Perf Interrupt" +;++ +; +; VOID +; Cbus1PerfInterrupt( +; VOID +; ); +; +; Routine Description: +; +; This routine is the interrupt handler for the Cbus1 performance +; counter interrupt at a priority just below that of normal clocks. +; Its function is to update the global performance counter so that +; KeQueryPerformanceCounter can return meaningful values. +; +; This routine is executed only by one processor at a rate of seventeen +; times per second, as there is no need for all processors to update +; the same global counter. The only reason the rate is so high is +; because the interrupt interval must fit into a 16 bit register in the +; 8254. Otherwise, it would have been more like once per second. +; +; Since this routine is entered directly via an interrupt gate, interrupt +; protection via cli is not necessary. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST hipi_a, hipi_t + +cPublicProc _Cbus1PerfInterrupt ,0 + + ; + ; Save machine state on trap frame + ; + + ENTER_INTERRUPT hipi_a, hipi_t + + ; keep it simple, just issue the EOI right now. + ; no changing of taskpri/irql is needed here. + ; Thus, the EOI serves as the HalEndSystemInterrupt. + + mov eax, CBUS1_PERF_TASKPRI ; mark interrupting vector + CBUS_EOI eax, ecx ; destroy eax & ecx + + ; + ; (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 + + lea esi, _Halp8254Lock + + align 4 +Kcpi00: + ACQUIRE_SPINLOCK esi, Kcpi01 + + ; + ; Update Cbus1 performance counter if a performance counter query + ; hasn't done so already. + ; + + cmp dword ptr [Cbus18254Late], 0 + jne short @f + + add Cbus1PerfCounterLow, ROLLOVER_COUNT ; update performance counter + adc Cbus1PerfCounterHigh, 0 + jmp short noroll + + align 4 +@@: + dec dword ptr [Cbus18254Late] + + align 4 +noroll: + RELEASE_SPINLOCK esi + + ; + ; Call this directly instead of through INTERRUPT_EXIT + ; because the HalEndSystemInterrupt has already been done, + ; and must only be done ONCE per interrupt. + ; + + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi + + align 4 +Kcpi01: + SPIN_ON_SPINLOCK esi,<Kcpi00> + +stdENDP _Cbus1PerfInterrupt + +;++ +; +; ULONG +; Cbus1SetTimeIncrement ( +; IN ULONG DesiredIncrement +; ) +; +; /*++ +; +; Routine Description: +; +; This routine initializes the system time clock to generate an +; interrupt at every DesiredIncrement interval. +; +; Arguments: +; +; DesiredIncrement - desired interval between every timer tick in +; 100ns units. +; +; Return Value: +; +; The *REAL* time increment set - this can be different from what he +; requested due to hardware limitations - currently to keep the math +; simple, we limit the interval to between 1 and 32000 milliseconds, +; on millisecond boundaries (ie 1.3 milliseconds is rounded to 1 +; millisecond). +;-- + +cPublicProc _Cbus1SetTimeIncrement,1 + + mov eax, [esp+4] ; caller's desired setting + xor edx, edx + mov ecx, 10000 + div ecx ; round to milliseconds + + cmp eax, MAXCLOCK_RATE_IN_MS ; desired > max? + jc short @f + mov eax, MAXCLOCK_RATE_IN_MS ; yes, use max +@@: + + or eax, eax ; MS < min? + jnz short @f + inc eax ; yes, use min +@@: + + mov CbusClockRateInMillis, eax ; set new rate in milliseconds + + ; + ; inform this processor's local APIC hardware of the change. + ; and then tell all the other processors to update theirs too... + ; + stdCall _Cbus1ProgramClock + + mov eax, Cbus1CurrentTimeIncrement + + stdRET _Cbus1SetTimeIncrement + +stdENDP _Cbus1SetTimeIncrement + + 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. +; +; Note spurious interrupts are always sent to the spurious interrupt IDT +; entry, and hence, no need to check for one here. +; +; See also Cbus1ClockInterruptPx() - it's used by the non-boot processors, +; since additional processors don't need to bump any global counters. +; +; Arguments: +; +; None +; Interrupt is disabled +; +; Return Value: +; +; must set up eax with the increment value, also leave ebp pointing at +; base of trap frame. +; Does not return, jumps directly to KeUpdateSystemTime, which returns +; +; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt +; +;-- + ENTER_DR_ASSIST Hck_a, Hck_t + +cPublicProc _Cbus1ClockInterrupt ,0 + + ; + ; Save machine state in trap frame + ; (esp) - base of trap frame + ; + + ENTER_INTERRUPT Hck_a, Hck_t + + ; + ; Dismiss interrupt and raise irq level to clock2 level + ; + + push _CbusClockVector + sub esp, 4 ; allocate space to save OldIrql + stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL, _CbusClockVector, esp> + + POKE_LEDS eax, edx + + ; + ; (esp) = OldIrql + ; (esp+4) = Vector + ; (esp+8) = base of trap frame + ; (ebp) = address of trap frame + ; (eax) = time increment + ; + mov eax, Cbus1CurrentTimeIncrement + + jmp _KeUpdateSystemTime@0 + +stdENDP _Cbus1ClockInterrupt +_TEXT ends + + end diff --git a/private/ntos/nthals/halcbus/i386/cb2stall.asm b/private/ntos/nthals/halcbus/i386/cb2stall.asm new file mode 100644 index 000000000..7be774f4f --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cb2stall.asm @@ -0,0 +1,1347 @@ + + title "Interval Clock Interrupt" +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; cb2stall.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 declared 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 +include cbus.inc + .list + + EXTRNP _HalEndSystemInterrupt,2 + EXTRNP _HalBeginSystemInterrupt,3 +if DBG + EXTRNP _HalDisplayString,1 +endif +ifdef CBC_REV1 + EXTRNP _Cbus2RequestSoftwareInterrupt,1 +endif + +; +; 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_MODE3 EQU 6 ; Use mode 3 +COMMAND_8254_BCD EQU 0 ; Binary count down +COMMAND_8254_LATCH_READ EQU 0 ; Latch read command + +PERFORMANCE_FREQUENCY EQU 10000000 + +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 ; + +CMOS_CONTROL_PORT EQU 70h ; command port for cmos +CMOS_DATA_PORT EQU 71h ; cmos data port +D_INT032 EQU 8E00h ; access word for 386 ring 0 int gate + +; +; ==== Values used for System Clock ==== +; + + +_DATA SEGMENT DWORD PUBLIC 'DATA' + +; +; The following array stores the per microsecond loop count for each +; central processor. +; + + public HalpPerfCounterLow, HalpPerfCounterHigh +if DBG + public CbusClockCount + public CbusClockLate + public _CbusCatchClock +endif +Cbus2PerfInit dd 0 +HalpPerfCounterLow dd 0 +HalpPerfCounterHigh dd 0 +if DBG +CbusClockCount dd 0 +CbusClockLate dd 0 +_CbusCatchClock dd 0 +endif + + public Cbus2NumberSpuriousClocks +Cbus2NumberSpuriousClocks dd 0 + + public HalpCurrentRollOver, HalpCurrentTimeIncrement +HalpCurrentRollOver dd 0 +HalpCurrentTimeIncrement dd 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 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 10 ; Table goes to 15MS, but since we + ; use only 486 & above, limit to 10ms. +HalpNextMSRate dd 0 +HalpPendingMSRate dd 0 + +; +; This value is used by CPUx (x>0) during the clock interrupt +; routine to re-trigger the call to KeUpdateRunTime. Processors +; calling KeUpdateRunTime call at the maximum supported rate as +; reported by KeSetTimeIncrement. +; (HAL Development Reference Guide 5.12) +; + public HalpMaxTimeIncrement +HalpMaxTimeIncrement dd 0 + +; +; Holds the next time increment. HalpCurrentTimeIncrement is +; initialized with this value just before calling KeUpdateSystemTime. +; + public HalpNewTimeIncrement +HalpNewTimeIncrement dd 0 + +if DBG +; +; Cbus2MaxTimeStamp... max value of time stamp ever. +; Cbus2MinTimeStamp... min value of time stamp ever. +; Cbus2CountOverflowTimeStamp...# of overflows of time stamp. +; Cbus2SumTimeStampHi... Sum of all time stamps. +; Cbus2SumTimeStampLow... +; Cbus2CountClockInt... # of clock interrupts. +; + public Cbus2MaxTimeStamp, Cbus2MinTimeStamp, + Cbus2CountOverflowTimeStamp, Cbus2SumTimeStampHi, + Cbus2SumTimeStampLow, Cbus2CountClockInt + +Cbus2MaxTimeStamp dd 0 +Cbus2MinTimeStamp dd -1 +Cbus2CountOverflowTimeStamp dd 0 +Cbus2SumTimeStampHi dd 0 +Cbus2SumTimeStampLow dd 0 +Cbus2CountClockInt dd 0 +endif + +_DATA ends + + +INIT SEGMENT PARA PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +if DBG +RTC_toofast db 'RTC IRQ8 HARDWARE ERROR\n', 0 +endif + + page ,132 + subttl "Initialize Clock" +;++ +; +; VOID +; Cbus2InitializeClock ( +; ) +; +; 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. +; +; All processors call this routine, but only the first call needs +; to do anything. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- +cPublicProc _Cbus2InitializeClock ,0 + cmp dword ptr PCR[PcHal.PcrNumber], 0 + + jz @f + + ; + ; Initialize the 'TickOffset' field for CPUx (x>0). + ; It indicates the number of nsec left before calling the + ; KeUpdateRunTime routine. This routine is called at the + ; maximum supported rate as reported by KeSetTimeIncrement. + ; + mov eax, HalpMaxTimeIncrement + mov PCR[PcHal.PcrTickOffset], eax + + stdRET _Cbus2InitializeClock + +@@: + 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 +; + + ; + ; This value is used by CPUx (x>0) during the clock interrupt + ; routine to re-trigger the call to KeUpdateRunTime. Processors + ; calling KeUpdateRunTime call at the maximum supported rate as + ; reported by KeSetTimeIncrement. + ; (HAL Development Reference Guide 5.12) + ; + mov HalpMaxTimeIncrement, edx + + push eax + stdCall _KeSetTimeIncrement, <edx, ecx> + pop ecx + + pushfd ; save caller's eflag + cli ; make sure interrupts are disabled + +; +; Set clock rate +; (ecx) = RollOverCount +; + + ; + ; use a 50% duty cycle for the system clock + ; + mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE3 + 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 + + ; + ; Set up the performance counter mechanism as well: + ; Zero the global system timer for all of the processors. + ; + mov eax, dword ptr [_CbusTimeStamp] + mov dword ptr [eax], 0 + mov Cbus2PerfInit, 1 ; calibration done + + stdRET _Cbus2InitializeClock + +stdENDP _Cbus2InitializeClock + +INIT ends + +_TEXT$03 SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + 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 _Cbus2ClockInterrupt ,0 + +; +; Save machine state in trap frame +; + + ENTER_INTERRUPT Hci_a, Hci_t + +; +; (esp) - base of trap frame +; + +; +; Note that during this phase the interrupts are already disabled. +; This is important because we don't want to take an IPI during this phase +; which may force us to discard the next clock interrupt. +; The call to HalBeginSystemInterrupt below will enable the interrupts. +; + +ifdef MCA +; +; Special hack for MCA machines +; + + in al, 61h + jmp $+2 + or al, 80h + out 61h, al + jmp $+2 + +endif ; MCA + + cmp [_Cbus2CheckSpuriousClock], 1 ; Check for spurious clock? + je Hci200 +Hci05: + +ifdef CBC_REV1 + ; + ; because we can miss an interrupt due to a hardware bug in the + ; CBC rev 1 silicon, send ourselves an IPI on every clock. + ; since we don't know when we've missed one, this will ensure + ; we don't cause lock timeouts if nothing else! + ; + + stdCall _Cbus2RequestSoftwareInterrupt, <IPI_LEVEL> +endif + +; +; Dismiss interrupt and raise irq level to clock2 level +; + +Hci10: + push _CbusClockVector + sub esp, 4 ; allocate space to save OldIrql + stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL, _CbusClockVector, esp> + POKE_LEDS eax, edx + +if DBG + inc dword ptr [CbusClockCount] +endif + ; + ; Update our software 64-bit performance counter and zero the + ; 32-bit high resolution CBC timestamp counter. we'd like to + ; read off the real amount of elapsed time, ie: + ; + ; mov eax, dword ptr [ecx] + ; add HalpPerfCounterLow, eax + ; + ; but we can't because when the debugger is enabled, NT holds off + ; interrupts for long periods of time, ie: like in DbgLoadImageSymbols() + ; for over 700 _milliseconds_ !!!. so just fib like all the other + ; HALs do, and tell NT only 10ms have gone by. + ; +if DBG + ; + ; we had a problem where clock interrupts are getting + ; held off for approximately 700 ms once per second! here is + ; the debug code which caught DbgLoadImageSymbols. + ; + ;mov ecx, dword ptr [_CbusTimeStamp] + ;mov eax, dword ptr [ecx] + + ;cmp _CbusCatchClock, 0 ; debug breakpoint desired? + ;je short @f + + ;cmp eax, 2000000 ; if more than 200 milliseconds since + ; the last clockintr, then trigger + ; the analyzer and go into debug + ;jb short @f + ;inc dword ptr [CbusClockLate] ; trigger analyzer + ;int 3 +@@: +endif + + mov eax, HalpCurrentTimeIncrement ; current time increment + mov HalpNewTimeIncrement, eax ; next time increment + +Hci30: + +; +; (esp) = OldIrql +; (esp+4) = Vector +; (esp+8) = base of trap frame +; ebp = trap frame +; eax = current time increment +; + + cmp HalpNextMSRate, 0 ; New clock rate desired? + jnz short Hci60 ; Yes, program timer h/w. + +Hci40: + + mov ebx, HalpNewTimeIncrement + +; +; Update performance counters. +; Do not change without reason the order of the following instractions. +; They are crafted in this way for the HalQueryPerformanceCounter +; routine (it can run at the same time on a different processor). +; The interrupts are disabled because we don't want to take an IPI +; during this process. +; + +; +; eax = current time increment +; ebx = next time increment +; + + mov ecx, dword ptr [_CbusTimeStamp] + cmp [_Cbus2CheckSpuriousClock], 1 ; Calculate new SystemTimer. + pushfd ; Save and disable flags. + cli + je Hci210 + sub edx, edx +Hci50: + +; +; Awkward ordering done to place the resetting of the SystemTimer +; as close to the update of the 64-bit performance counter -- +; + + mov dword ptr [ecx], edx ; New TimeStamp value. + add HalpPerfCounterLow, eax + adc HalpPerfCounterHigh, dword ptr 0 + mov HalpCurrentTimeIncrement, ebx ; Next time increment. + + popfd ; Restore flags. + +; +; (esp) = OldIrql +; (esp+4) = Vector +; (esp+8) = base of trap frame +; ebp = trap frame +; eax = time increment +; + + jmp _KeUpdateSystemTime@0 ; dispatch this tick + +Hci60: +; +; Time of clock frequency is being changed. See if the 8254 was +; was reprogrammed for a new rate during last tick +; +; (eax) = time increment for current tick + + cmp HalpPendingMSRate, 0 ; Was a new rate set durning last + jnz short Hci80 ; tick? Yes, go update globals + +Hci70: + +; +; 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 following rate : ~(old rate + new rate)/2 because the new +; count will be loaded at the end of the current half-cycle. +; +; (eax) = time increment for current tick + + mov ebx, HalpNextMSRate + mov HalpPendingMSRate, ebx ; pending rate + + ; + ; Next tick increment = ~(current TimeIncr + new TimeIncr)/2 + ; + mov ecx, HalpNewTimeIncrement + add ecx, HalpRollOverTable[ebx*8-8].TimeIncr + shr ecx, 1 + mov HalpNewTimeIncrement, ecx + + mov ecx, HalpRollOverTable[ebx*8-8].RollOver + + ; + ; Set clock rate + ; (ecx) = RollOverCount + ; + + push eax ; save current tick's rate + + ; + ; use a 50% duty cycle for the system clock + ; + mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE3 + 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 + jmp short Hci40 ; dispatch this tick + +Hci80: + +; +; 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 HalpNewTimeIncrement, edx ; next tick rate + mov HalpPendingMSRate, 0 ; no longer pending, clear it + + cmp ebx, HalpNextMSRate ; new rate == NextRate? + jne short Hci70 ; no, go set new pending rate + + mov HalpNextMSRate, 0 ; we are at this rate, clear it + jmp Hci30 ; process this tick + +Hci100: + add esp, 8 ; spurious, no EndOfInterrupt + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi + +; +; Check if this is a spurious interrupt. +; +Hci200: + mov ecx, dword ptr [_CbusTimeStamp] + mov edx, dword ptr [ecx] ; Get its value + sub edx, HalpCurrentTimeIncrement ; In range ? + jb short Hci240 ; No, this is spurious. + jmp Hci05 + +; +; Ensure that the time stamp is no larger than HalpCurrentTimeIncrement. +; We couldn't perform this code earlier when Hci200 was called +; because it would open a window where the System Timer has been +; reset, but the 64-bit performance counter has not been updated. +; An additional CPU, in this case, could get a smaller value than +; previously requested. To minimize this case, the resetting of +; the SystemTimer should be as close to the 64-bit performance counter +; as possible. +; +; in: ecx = time stamp address. +; eax = current time increment. +; ebx = next time increment. +; +; out: ecx = time stamp address. +; eax = current time increment. +; ebx = next time increment. +; edx = new time stamp value to set. +; + +Hci210: + mov edx, dword ptr [ecx] ; Get its value + sub edx, eax ; Remove current increment. +if DBG + ; + ; Collect data for average. + ; + cmp edx, 20000000 ; Debugging if above 2 sec. + ja short @f + inc Cbus2CountClockInt + add Cbus2SumTimeStampLow, edx + adc Cbus2SumTimeStampHi, 0 +@@: + ; + ; Compute minimum. + ; + cmp Cbus2MinTimeStamp, edx + jbe short @f + mov Cbus2MinTimeStamp, edx +@@: +endif + cmp edx, eax ; Time stamp must be <= unit. + jbe Hci50 ; Yes, process the int. +if DBG + ; + ; Compute maximum. + ; + cmp edx, 20000000 ; Debugging if above 2 sec. + ja short @f + inc Cbus2CountOverflowTimeStamp + cmp Cbus2MaxTimeStamp, edx + ja short @f + mov Cbus2MaxTimeStamp, edx +@@: +endif + +; +; Use the whole time increment. +; + mov edx, eax + jmp Hci50 + +; +; This is a spurious interrupt. +; +Hci240: + inc [Cbus2NumberSpuriousClocks] + mov eax, [_Cbus2ClockVector] ; get the interrupting vector + CBUS_EOI eax, edx ; ack the interrupt controller + + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi + +stdENDP _Cbus2ClockInterrupt + + page ,132 + subttl "System Clock Interrupt for Additional Processors" +;++ +; +; Routine Description: +; +; This routine is entered as the result of an interrupt generated by CLOCK. +; This routine is entered only by additional (non-boot) processors, so it +; must NOT update the performance counter or update the system time. +; +; instead, it just dismisses the interrupt, raises system Irql to +; CLOCK2_LEVEL and transfers control to the standard system routine +; to update the execution time of the current thread and process. +; +; Arguments: +; +; None +; Interrupt is disabled +; +; Return Value: +; +; Does not return, jumps directly to KeUpdateRunTime, which returns +; +; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt +; +;-- + ENTER_DR_ASSIST Hcix2_a, Hcix2_t + +cPublicProc _Cbus2ClockInterruptPx ,0 + + ; + ; Save machine state in trap frame + ; (esp) - base of trap frame + ; + + ENTER_INTERRUPT Hcix2_a, Hcix2_t + + mov eax, HalpCurrentTimeIncrement ; Get the clock period. + mov ecx, dword ptr [_CbusTimeStamp] ; Get the time stamp address. + + ; + ; Note that during this phase the interrupts are already disabled. + ; This is important because we don't want to take an IPI during this + ; phase which may force us to discard the next clock interrupt. + ; The call to HalBeginSystemInterrupt below will enable the interrupts. + ; + + cmp [_Cbus2CheckSpuriousClock], 1 ; Check for spurious clock? + je short Hcix70 + sub edx, edx ; +Hcix50: + mov dword ptr [ecx], edx ; New SystemTimer value. + + ; + ; We call the KeUpdateRunTime routine only at the maximum + ; rate reported by KeSetTimeIncrement. + ; + sub PCR[PcHal.PcrTickOffset], eax ; subtract time increment. + jg short Hcix100 ; if greater, not complete tick. + mov eax, HalpMaxTimeIncrement ; Re-trigger the call. + add PCR[PcHal.PcrTickOffset], eax ; + +ifdef CBC_REV1 + ; + ; because we can miss an interrupt due to a hardware bug in the + ; CBC rev 1 silicon, send ourselves an IPI on every clock. + ; since we don't know when we've missed one, this will ensure + ; we don't cause lock timeouts if nothing else! + ; + + stdCall _Cbus2RequestSoftwareInterrupt, <IPI_LEVEL> +endif + + ; + ; Dismiss interrupt and raise irq level to clock2 level + ; + + push _CbusClockVector + sub esp, 4 ; allocate space to save OldIrql + stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL, _CbusClockVector, esp> + + ; Spurious interrupts on Corollary hardware are + ; directed to a different interrupt gate, so no need + ; to check return value above. + + POKE_LEDS eax, edx + + ; + ; (esp) = OldIrql + ; (esp+4) = Vector + ; (esp+8) = base of trap frame + ; + ; (ebp) = base of trap frame for KeUpdateRunTime, this was set + ; up by the ENTER_INTERRUPT macro above + + stdCall _KeUpdateRunTime,<dword ptr [esp]> + + INTERRUPT_EXIT +; +; Check if this is a spurious interrupt. +; +; in: ecx = time stamp address. +; eax = current time increment. +; +; out: ecx = time stamp address. +; eax = current time increment. +; edx = new time stamp value to set. +; +Hcix70: + mov edx, dword ptr [ecx] ; Get its value + sub edx, eax ; In range ? + jb short Hcix100 ; No, this is spurious. + cmp edx, eax ; Time stamp must be <= unit. + jbe short Hcix50 ; Yes, process the int. +; +; Use the whole time-stamp unit. +; + mov edx, eax + jmp short Hcix50 + +; +; This is a spurious interrupt. +; +Hcix100: + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without EOI + +stdENDP _Cbus2ClockInterruptPx + +;++ +; +; ULONG +; Cbus2SetTimeIncrement ( +; IN ULONG DesiredIncrement +; ) +; +; /*++ +; +; Routine Description: +; +; This routine initializes the system time clock to generate an +; interrupt at every DesiredIncrement interval. No lock synchronization +; is needed here, since it is done by the executive prior to calling us. +; +; Arguments: +; +; DesiredIncrement - desired interval between every timer tick (in +; 100ns unit.) +; +; Return Value: +; +; The *REAL* time increment set. +;-- +cPublicProc _Cbus2SetTimeIncrement,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 eax, HalpRollOverTable[eax*8-8].TimeIncr + stdRET _Cbus2SetTimeIncrement + +stdENDP _Cbus2SetTimeIncrement + + page ,132 + subttl "Query Performance Counter" +;++ +; +; LARGE_INTEGER +; Cbus2QueryPerformanceCounter ( +; 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+4] ; User supplied Performance Frequency + +cPublicProc _Cbus2QueryPerformanceCounter ,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 performance counter calibration has occured. + ; + cmp Cbus2PerfInit, 0 ; calibration finished yet? + jne @f ; yes, we can read the perf counter + + ; + ; Initialization hasn't occurred yet, so just return zeroes. + ; + mov eax, 0 + mov edx, 0 + jmp ret_freq + +@@: + ; + ; Merge our software 64-bit performance counter and the + ; 32-bit high resolution CBC timestamp counter into edx:eax + ; + + push ebx ; ebx will be destroyed +@@: + mov ebx, HalpCurrentTimeIncrement + + mov edx, HalpPerfCounterHigh + mov eax, HalpPerfCounterLow + mov ecx, dword ptr [_Cbus2TimeStamp0] + mov ecx, dword ptr [ecx] + + ; + ; re-read the global counters until we see we didn't get a torn read. + ; + cmp ebx, HalpCurrentTimeIncrement + jne short @b + + cmp edx, HalpPerfCounterHigh + jne short @b + + cmp eax, HalpPerfCounterLow + jne short @b + + ; + ; This code can run on any CPU while the 'perf counters' + ; are updated only by CPU 0. If the first CPU is unable + ; to receive the clock interrupt because it is 'cli-ed' + ; or 'ipi-ed' for a long time, we need to return a value + ; greater than the provious returned value but less or equal + ; to any future value. + ; + cmp ecx, ebx ; Time stamp must be <= unit + jbe short @f ; Yes, it is + mov ecx, ebx ; else use unit +@@: + pop ebx ; Restore ebx + + ; + ; since the time stamp register and our 64-bit software register + ; are already both in 100ns units, there's no need for conversion here. + ; + add eax, ecx + adc edx, dword ptr 0 + +ret_freq: + + ; + ; return value is in edx:eax, return the performance counter + ; frequency if the caller wants it. + ; + + or dword ptr KqpcFrequency, 0 ; is it a NULL variable? + jz short @f ; it's NULL, so bail + + mov ecx, KqpcFrequency ; (ecx)-> Frequency variable + + ; + ; Set frequency to a hardcoded clock speed of 66Mhz for now. + ; + mov DWORD PTR [ecx], PERFORMANCE_FREQUENCY + mov DWORD PTR [ecx+4], 0 + +@@: + stdRET _Cbus2QueryPerformanceCounter + +stdENDP _Cbus2QueryPerformanceCounter + +_TEXT$03 ends + +; 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 + +; +; CMOS_WRITE +; +; Description: This macro read 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 + +INIT SEGMENT PARA PUBLIC 'CODE' ; Start 32 bit code + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + + page ,132 + subttl "Initialize Stall Execution Counter" +;++ +; +; VOID +; Cbus2InitializeStall ( +; IN CCHAR ProcessorNumber +; ) +; +; Routine Description: +; +; This routine initialize the per Microsecond counter for +; KeStallExecutionProcessor. Note that the additional processors +; execute this loop in Phase1, so they are already getting clock +; interrupts as well as the RTC interrupts they expect from this +; routine. We should disable clock interrupts during this period +; to ensure a really accurate result, but it should be "good enough" +; for now. +; +; Arguments: +; +; ProcessorNumber - Processor Number +; +; Return Value: +; +; None. +; +;-- + +KiseInterruptCount equ [ebp-12] ; local variable + +cPublicProc _Cbus2InitializeStall ,1 + + 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 every 125ms at + ; IRQ 8. + ; + + cli ; make sure interrupts are disabled + + ; + ; Since RTC interrupt will come from IRQ 8, we need to save + ; the 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] ; (edx)->IDT + + ; + ; the profile vector varies for each platform + ; + mov eax, dword ptr [_ProfileVector] + + 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. + ; we actually will retrieve this value twice, but only pop it once. + ; + push ecx ; (TOS) -> &IDT[HalProfileVector] + + ; + ; No need to get and save current interrupt masks - only IPI + ; and software interrupts are enabled at this point in the kernel + ; startup. since other processors are still in reset, the profile + ; interrupt is the only one we will see. we really don't need to save + ; the old IDT[PROFILE_VECTOR] entry either, by the way - it's just + ; going to be overwritten in HalInitSystem Phase 1 anyway. + ; + ; note we are not saving edx before calling this function + + stdCall _HalEnableSystemInterrupt,<dword ptr [_ProfileVector],PROFILE_LEVEL,0> + + mov ecx, dword ptr [esp] ; restore IDT pointer + + 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 + + ; + ; register C _MUST_ be read before register B is initialized, + ; otherwise an interrupt which was already pending will happen + ; immediately. + ; + mov al,0CH ; Register C + CMOS_READ ; Read to initialize + ; + ; 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,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 the 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 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 + + ; + ; register C _MUST_ be read before register B is initialized, + ; otherwise an interrupt which was already pending will happen + ; immediately. + ; + mov al,0CH ; Register C + CMOS_READ ; Read to initialize + + ; + ; 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,0DH ; Register D + CMOS_READ ; Read to initialize + + mov eax, _ProfileVector ; mark interrupting vec + CBUS_EOI eax, ecx ; destroy eax & ecx + + xor eax, eax ; reset loop counter + + stdCall _HalpReleaseCmosSpinLock + + iretd + + align 4 +kise25: + + +if DBG + ; + ; ** temporary - check for incorrect KeStallExecutionProcessorLoopCount + ; + cmp eax, 0 + jnz short kise30 + stdCall _DbgBreakPoint + + ; never return + ; + ; ** End temporary code + ; + +kise30: +endif + 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 + + align 4 +kise40: + + mov PCR[PcStallScaleFactor], eax + + ; + ; Reset return address to kexit + ; + + pop eax ; discard original return address + push offset FLAT:kexit ; return to kexit + + ; + ; Shutdown the periodic RTC 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 + + stdCall _HalDisableSystemInterrupt,<dword ptr [_ProfileVector],PROFILE_LEVEL> + mov eax, _ProfileVector ; mark interrupting vec + CBUS_EOI eax, ecx ; destroy eax & ecx + + and word ptr [esp+8], NOT 0200H ; Disable interrupt upon return + iretd + + align 4 +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 + + popfd ; restore caller's eflags + mov esp, ebp + pop ebp ; restore ebp + stdRET _Cbus2InitializeStall + +stdENDP _Cbus2InitializeStall + +INIT ends + + end diff --git a/private/ntos/nthals/halcbus/i386/cbapic.c b/private/ntos/nthals/halcbus/i386/cbapic.c new file mode 100644 index 000000000..ab12868e1 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbapic.c @@ -0,0 +1,898 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbapic.c + +Abstract: + + This module implements the initialization of the APIC in Corollary + Cbus1 and Cbus2 systems. Note that Cbus1 uses only the APIC, where + Cbus2 can use either the APIC or the CBC - the HAL is told (by RRD) which + interrupt controller to use when operating under Windows NT. + +Author: + + Landy Wang (landy@corollary.com) 05-Oct-1992 + +Environment: + + Kernel mode only. + +Revision History: + + +--*/ + +#include "halp.h" +#include "cbusrrd.h" // HAL <-> RRD interface definitions +#include "cbus.h" // Cbus1 & Cbus2 max number of elements is here +#include "cbus1.h" // Cbus1 & Cbus2 max number of elements is here +#include "cbus_nt.h" // C-bus NT-specific implementation stuff +#include "bugcodes.h" +#include "stdio.h" +#include "cbusnls.h" +#include "cbusapic.h" + +VOID +ApicArbSync( +VOID +); + +VOID +CbusApicBrandIOUnitID( +IN ULONG Processor +); + +VOID +CbusInitializeLocalApic( +IN ULONG Processor, +IN PVOID PhysicalApicLocation, +IN ULONG SpuriousVector +); + + +VOID +CbusInitializeIOApic( +IN ULONG Processor, +IN PVOID PhysicalApicLocation, +IN ULONG RedirVector, +IN ULONG RebootVector, +IN ULONG IrqPolarity +); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, CbusApicBrandIOUnitID) +#pragma alloc_text(INIT, CbusInitializeLocalApic) +#pragma alloc_text(INIT, CbusInitializeIOApic) +#endif + +ULONG +READ_IOAPIC_ULONG(ULONG, ULONG); + +VOID +WRITE_IOAPIC_ULONG(ULONG, ULONG, ULONG); + +VOID +CbusApicRedirectionRequest(PULONG); + +VOID +CbusDisable8259s( USHORT ); + +VOID +HalpSpuriousInterrupt(VOID); + +VOID +IOApicUpdate( VOID ); + +VOID +CbusApicArbsync(VOID); + +VOID +CbusRebootHandler( VOID ); + +extern PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1]; + +extern ULONG CbusBootedProcessors; + +ULONG CbusIOApicCount; +PVOID CbusIOApic[MAX_CBUS_ELEMENTS]; +PAPIC_REGISTERS CbusLocalApic; + +// +// used for IPI vector enabling/disabling since each I/O +// APIC is visible only to its attached processor. +// +REDIR_PORT_T CbusApicRedirPort[MAX_ELEMENT_CSRS]; + +VOID +CbusApicBrandIOUnitID( +IN ULONG Processor +) +/*++ + +Routine Description: + + Each processor assigns an APIC ID to his I/O APIC so + it can arbitrate for the APIC bus, etc. Intel documentation + says that every local and I/O APIC must have a unique id. + +Arguments: + + Processor - Supplies a logical processor number + +Return Value: + + None. + +--*/ +{ + WRITE_IOAPIC_ULONG(0, IO_APIC_ID_OFFSET, (2 * Processor) << APIC_BIT_TO_ID); + + CbusApicArbsync(); +} + +/*++ + +Routine Description: + + Called to get the EOI address for this particular vector. + +Arguments: + + Vector - Supplies the APIC vector that will generate this interrupt + +Return Value: + + The EOI address needed for this vector. + +--*/ +PULONG +CbusApicVectorToEoi( +IN ULONG Vector +) +{ + UNREFERENCED_PARAMETER(Vector); + + return (PULONG)(&CbusLocalApic->ApicEOI); +} + +/*++ + +Routine Description: + + Called by each processor to initialize his local APIC. + The first processor to run this routine will map the + local APICs for all processors. + + Note that all interrupts are blocked on entry since + we are being called from HalInitializeProcessor(). + +Arguments: + + Processor - Supplies a logical processor number + +Return Value: + + None. + +--*/ +VOID +CbusInitializeLocalApic( +IN ULONG Processor, +IN PVOID PhysicalApicLocation, +IN ULONG SpuriousVector +) +{ + ULONG ProcessorBit; + ULONG ApicIDBit; + REDIRECTION_T RedirectionEntry = { 0 }; + + // + // If the APIC mapping has not been set up yet, + // do it now. Given the NT startup architecture, + // this will always be done by the boot processor. + // + // We map in the APIC into global space instead of in + // the PCR because all processors see it at the + // same _physical_ address. Note the page is mapped PWT. + // + + // + // Note that all idle threads will share a common + // page directory, and the HAL PDE is inherited when + // new processes are created. Hence, a single + // HalpMapMemory for the APIC is enough for all + // processors to be able to see their APICs. + // + if (!CbusLocalApic) { + CbusLocalApic = (PAPIC_REGISTERS) HalpMapPhysicalMemoryWriteThrough ( + PhysicalApicLocation, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + PhysicalApicLocation, LOCAL_APIC_SIZE)); + } + + (PTASKPRI) KeGetPcr()->HalReserved[PCR_TASKPRI] = + &CbusLocalApic->ApicTaskPriority; + + // + // Here we initialize our destination format and + // logical destination registers so that we can get IPIs + // from other processors. + // + // Specify full decode mode in the destination format register - + // ie: each processor sets only his own bit, and a "match" requires + // that at least one bit match. The alternative is encoded mode, + // in which _ALL_ encoded bits must match the sender's target for + // this processor to see the sent IPI. + // + CbusLocalApic->ApicDestinationFormat = APIC_ALL_PROCESSORS; + + // + // the logical destination register is what the redirection destination + // entry compares against. only the high 8 bits will be supported + // in Intel's future APICs, although this isn't documented anywhere! + // + ProcessorBit = KeGetPcr()->HalReserved[PCR_BIT]; + + ApicIDBit = (ProcessorBit << APIC_BIT_TO_ID); + + CbusLocalApic->ApicLogicalDestination = ApicIDBit; + + // + // designate the spurious interrupt vector we want to see, + // and inform this processor's APIC to enable interrupt + // acceptance. + // + CbusLocalApic->ApicSpuriousVector = + SpuriousVector | LOCAL_APIC_ENABLE; + + // + // as each processor comes online here, we must have ALL + // processors resync their arbitration IDs to take into + // account the new processor. note that we will set: + // arb id == APIC id == processor number. + // + // the strange ID setting is to satisfy Intel's need for + // uniqueness amongst I/O and local unit ID numbering. + // + + CbusLocalApic->LocalUnitID = ((2 * Processor + 1) << APIC_BIT_TO_ID); + + // + // sync up our new ID with everyone else + // + + CbusApicArbsync(); + + // + // Create the NMI routing linkage for this processor + // It is set as level sensitive, enabled and generating NMI trap 2. + // + + RedirectionEntry.ra.Trigger = APIC_LEVEL; + RedirectionEntry.ra.Mask = APIC_INTR_UNMASKED; + RedirectionEntry.ra.Delivery_mode = APIC_INTR_NMI; + RedirectionEntry.ra.Vector = 2; + RedirectionEntry.ra.Destination = ApicIDBit; + CbusLocalApic->ApicLocalInt1 = RedirectionEntry; + + // + // Create the spurious interrupt IDT entry for this processor + // + + KiSetHandlerAddressToIDT(SpuriousVector, HalpSpuriousInterrupt); + + // + // we must specify HIGH_LEVEL when we enable the spurious vector + // here because it will overwrite the CbusVectorToIrql[] entry + // for the HIGH_LEVEL (0xFF!). the spurious vector really only + // needs an IDT entry and doesn't need any other software tables, + // but make the enable call for tracking purposes. + // + HalEnableSystemInterrupt(SpuriousVector, HIGH_LEVEL, Latched); + + // + // start off at IRQL 0 - we are still protected by cli. + // + CbusLocalApic->ApicTaskPriority.rb.LowDword = 0; +} + +/*++ + +Routine Description: + + Note that all interrupts are blocked on entry since + this routine is called from HalInitializeProcessor. + Initialize this processor's local and I/O APIC units. + +Arguments: + + Processor - Supplies a logical processor number + +Return Value: + + None. + +--*/ +VOID +CbusInitializeIOApic( +IN ULONG Processor, +IN PVOID PhysicalApicLocation, +IN ULONG RedirVector, +IN ULONG RebootVector, +IN ULONG IrqPolarity +) +{ + ULONG ProcessorBit; + ULONG ApicIDBit; + ULONG ApicBusNumber; + ULONG RedirectionAddress; + REDIRECTION_T RedirectionEntry = { 0 }; + + if (CbusIOApicCount >= MAX_CBUS_ELEMENTS) { + return; + } + + CbusIOApic[CbusIOApicCount] = + (PVOID) HalpMapPhysicalMemoryWriteThrough ( + PhysicalApicLocation, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + PhysicalApicLocation, IO_APIC_SIZE)); + + CbusApicBrandIOUnitID(Processor); + + // + // Disable all 8259 inputs except the irq0 clock. + // remember the irq0 clock and the irq13 DMA + // chaining interrupts are internal to the Intel EISA + // chipset (specifically, the ISP chip), and if the HAL + // wants to enable them, it must be done here. + // This is done by enabling the 8259 ISP to send them + // to the processor(s) via the APIC. However, the Corollary HAL + // currently uses the local APIC timers for clocks. The irq0 + // clock is enabled solely for the performance counter because + // we want to use a separate clock for it, (rather than the system + // timer which creates race conditions). + // + // Note that all other EISA bus device interrupts only need to + // be enabled in the APIC for processors to see them. + // + CbusDisable8259s(0xFFFE); + + // + // All redirection table entries are disabled by default when the + // processor emerges from reset. Later, each entry is individually + // enabled from their respective drivers via HalEnableSystemInterrupt. + + // + // Indicate the APIC (not the 8259s) will now handle provide + // the interrupt vectors to the processor during an INTA cycle. + // This is done by writing to the APMode port. Note that at this + // time we will also sync the APIC polarity control registers with + // the ELCR. Since irq0 has no polarity control, the hardware + // uses bit0 for the APMode enable, so make sure this bit is on too. + // + + CbusLocalApic->APMode = (UCHAR)((IrqPolarity & 0xFF) | 0x1); + CbusLocalApic->PolarityPortHigh = (UCHAR)((IrqPolarity >> 8) & 0xFF); + + // + // Create an interrupt gate so other processors can + // let the boot processor know about desired I/O APIC + // modifications (ie: enabling & disabling interrupts). + // This is necessary since each processor can only access + // his own I/O APIC, and only the boot processor's I/O APIC + // is attached to the EISA bus interrupt inputs. this only + // needs to be done once regardless of how many I/O APICs are + // present in the system. + // + + if (CbusIOApicCount == 0) { + KiSetHandlerAddressToIDT(RedirVector, IOApicUpdate); + HalEnableSystemInterrupt(RedirVector, IPI_LEVEL, Latched); + + KiSetHandlerAddressToIDT(RebootVector, CbusRebootHandler); + HalEnableSystemInterrupt(RebootVector, IPI_LEVEL, Latched); + } + +#define TRAP2 2 + + ProcessorBit = (ULONG) KeGetPcr()->HalReserved[PCR_BIT]; + + ApicIDBit = (ProcessorBit << APIC_BIT_TO_ID); + + /* + * support NMIs from the EISA bridge as trap 2. + */ + RedirectionEntry.ra.Mask = APIC_INTR_UNMASKED; + RedirectionEntry.ra.Trigger = APIC_LEVEL; + RedirectionEntry.ra.Dest_mode = APIC_LOGICAL_MODE; + RedirectionEntry.ra.Vector = TRAP2; + RedirectionEntry.ra.Destination = ApicIDBit; + RedirectionEntry.ra.Delivery_mode = APIC_INTR_FIXED; + + // + // support multiple I/O buses by initializiing + // our current bus number... + // + ApicBusNumber = CbusIOApicCount; + + RedirectionAddress = (ULONG)CbusApicLinkVector((PBUS_HANDLER)0, + (ULONG)-1, TRAP2); + + WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress + 1, + RedirectionEntry.ra.Destination); + WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress, + RedirectionEntry.rb.dword1); + + // + // we've initialized another I/O APIC... + // + CbusIOApicCount++; +} + +/*++ + +Routine Description: + + Enable the specified interrupt for the calling processor. + Remember only the boot processor can add/remove processors from + the I/O APIC's redirection entries. + + This operation is trivial for the boot processor. However, additional + processors must interrupt the boot processor with an "enable interrupt" + request and then spin waiting for the boot processor to acknowledge that + the entry has been modified. Note that the caller holds the HAL's + CbusVectorLock at CLOCK_LEVEL on entry. + +Arguments: + + Vector - Supplies a vector number to enable + + HardwarePtr - Supplies a redirection entry address + + LowestInGroup - TRUE if this vector should be sent to lowest-in-group + processor when the interrupt occurs. + +Return Value: + + None. + +--*/ +VOID +CbusEnableApicInterrupt( +IN ULONG ApicBusNumber, +IN ULONG Vector, +IN PVOID HardwarePtr, +IN ULONG FirstAttach, +IN BOOLEAN LowestInGroup, +IN BOOLEAN LevelTriggered +) +{ + ULONG Processor, ProcessorBit; + ULONG ApicIDBit; + ULONG ParticipatingProcessors; + REDIRECTION_T RedirectionEntry = { 0 }; + ULONG RedirectionAddress; + + ASSERT(ApicBusNumber < MAX_CBUS_ELEMENTS); + + RedirectionAddress = (ULONG)HardwarePtr; + + // + // Let the I/O APIC know that the calling processor wishes to + // participate in receipt of the interrupt. This must be done + // regardless of whether or not any other processors have already + // enabled the interrupt. + // + RedirectionEntry.ra.Vector = Vector; + + // + // Mark the caller's interrupt as level or edge triggered, + // based on the ELCR register we read earlier. + // + if (LevelTriggered == TRUE) { + RedirectionEntry.ra.Trigger = APIC_LEVEL; + } + else { + RedirectionEntry.ra.Trigger = APIC_EDGE; + } + + RedirectionEntry.ra.Mask = APIC_INTR_UNMASKED; + RedirectionEntry.ra.Dest_mode = APIC_LOGICAL_MODE; + + // + // Only enable APIC LIG arbitration delay (at least 4 cycles on + // our 10Mhz APIC bus, which is 0.4 microseconds) if our caller + // told us to do it and there is more than one processor in the + // machine. + // + + if (CbusProcessors > 1 && LowestInGroup == TRUE) { + RedirectionEntry.ra.Delivery_mode = APIC_INTR_LIG; + } + else { + RedirectionEntry.ra.Delivery_mode = APIC_INTR_FIXED; + } + + // + // Add this processor's bit field number to the + // I/O APIC in order to be considered for receipt of + // the interrupt. Note the CbusVectorLock must be held + // whilst issuing reads and writes to the I/O APIC. + // Remember, each processor can only access his own I/O APIC. + // + + Processor = (ULONG) KeGetPcr()->HalReserved[PCR_PROCESSOR]; + + ProcessorBit = (ULONG) KeGetPcr()->HalReserved[PCR_BIT]; + + ApicIDBit = (ProcessorBit << APIC_BIT_TO_ID); + + if (Processor == 0) { + + ParticipatingProcessors = + READ_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress + 1); + +#ifdef LANDY_DBG + DbgPrint("Boot processor enabling apic bus %d, vec %x, redir %x, 1stattach=%x, lig=%x, leveltrig=%x, prevpart=%x\n", + ApicBusNumber, + Vector, + HardwarePtr, + FirstAttach, + LowestInGroup, + LevelTriggered, ParticipatingProcessors); +#endif + + RedirectionEntry.ra.Destination = + (ParticipatingProcessors | ApicIDBit); + + WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress + 1, + RedirectionEntry.ra.Destination); + WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress, + RedirectionEntry.rb.dword1); + + } + else { + + // + // The boot processor controls the I/O APIC which distributes + // the interrupts. Only he can enable the interrupt for + // the calling processor, so send him an IPI now to + // fufill our request. This request must be satisfied before + // returning to our caller. + // + CbusApicRedirPort[Processor].Status = + (REDIR_ACTIVE_REQUEST | REDIR_ENABLE_REQUEST); + + CbusApicRedirPort[Processor].ApicID = ApicIDBit; + CbusApicRedirPort[Processor].BusNumber = ApicBusNumber; + CbusApicRedirPort[Processor].RedirectionAddress = + RedirectionAddress; + +#ifdef LANDY_DBG + DbgPrint("Processor %d enabling apic bus %d, vec %x, redir %x, 1stattach=%x, lig=%x, leveltrig=%x\n", + Processor, + ApicBusNumber, + Vector, + HardwarePtr, + FirstAttach, + LowestInGroup, + LevelTriggered); +#endif + + if (FirstAttach) { + CbusApicRedirPort[Processor].Status |= + REDIR_FIRSTATTACH_REQUEST; + CbusApicRedirPort[Processor].RedirectionCommand = + RedirectionEntry.rb.dword1; + CbusApicRedirPort[Processor].RedirectionDestination = + ApicIDBit; + } + + // + // Issue the command and wait for it to finish + // + + CbusApicRedirectionRequest((PULONG)&CbusApicRedirPort[Processor].Status); + +#ifdef LANDY_DBG + DbgPrint("Processor %d done waiting for apic enable vec %x\n", + Processor, Vector); +#endif + } +} + +VOID +CbusDisableApicInterrupt( +IN ULONG ApicBusNumber, +IN ULONG Vector, +IN PVOID HardwarePtr, +IN ULONG LastDetach +) +/*++ + +Routine Description: + + Disable the specified interrupt so it can not occur on the calling + processor upon return from this routine. Remember only the boot processor + can add/remove processors from his I/O APIC's redirection entries. + + This operation is trivial for the boot processor. However, additional + processors must interrupt the boot processor with a "disable interrupt" + request and then spin waiting for the boot processor to acknowledge that + the entry has been modified. Note that the caller holds the HAL's + CbusVectorLock at CLOCK_LEVEL on entry. + +Arguments: + + Vector - Supplies a vector number to disable + + HardwarePtr - Supplies a redirection entry address + + LastDetach - TRUE if this is the last processor to detach from the + specified vector + +Return Value: + + None. + +--*/ +{ + ULONG Processor, ProcessorBit, ApicIDBit; + REDIRECTION_T RedirectionEntry; + ULONG RedirectionAddress; + + ASSERT(ApicBusNumber < MAX_CBUS_ELEMENTS); + + RedirectionAddress = (ULONG)HardwarePtr; + + Processor = (ULONG) KeGetPcr()->HalReserved[PCR_PROCESSOR]; + + ProcessorBit = (ULONG) KeGetPcr()->HalReserved[PCR_BIT]; + + // convert processor logical bit number to Intel ID register format... + + ApicIDBit = (ProcessorBit << APIC_BIT_TO_ID); + + if (Processor) { +#ifdef LANDY_DBG + DbgPrint("Processor %d disabling apic bus %d, vec %x, redir %x \n", + Processor, + ApicBusNumber, + Vector, + HardwarePtr); +#endif + + // + // The boot processor controls the I/O APIC which distributes + // the interrupts. Since only he can disable the interrupt + // preventing it from coming to the calling processor, we + // IPI him here with our request. This request must be + // satisfied before returning to our caller. + // + CbusApicRedirPort[Processor].Status = (REDIR_ACTIVE_REQUEST | + REDIR_DISABLE_REQUEST); + if (LastDetach) { + CbusApicRedirPort[Processor].Status |= + REDIR_LASTDETACH_REQUEST; + } + + CbusApicRedirPort[Processor].ApicID = ApicIDBit; + CbusApicRedirPort[Processor].BusNumber = ApicBusNumber; + CbusApicRedirPort[Processor].RedirectionAddress = + RedirectionAddress; + + CbusApicRedirectionRequest((PULONG)&CbusApicRedirPort[Processor].Status); + +#ifdef LANDY_DBG + DbgPrint("Processor %d done waiting for apic disable vec %x\n", + Processor, Vector); +#endif + return; + } + + // + // Let the I/O APIC know that this CPU is no longer participating in + // receipt of the interrupt. Note the CbusVectorLock must be held + // whilst issuing reads and writes to the I/O APIC. + // + + RedirectionEntry.rb.dword1 = READ_IOAPIC_ULONG(ApicBusNumber, + RedirectionAddress); + + RedirectionEntry.rb.dword2 = READ_IOAPIC_ULONG(ApicBusNumber, + RedirectionAddress+1); + + // + // Remove this processor's bit field number from the interrupt + // participation list. If this is the last processor to detach, + // mask off the interrupt at the source as well, so no one will + // need to arbitrate for it should it get asserted later. + // + +#ifdef LANDY_DBG + DbgPrint("Boot Processor disabling apic bus %d, vec %x, redir %x, 1stattach=%x, lig=%x, leveltrig=%x\n", + ApicBusNumber, + Vector, + HardwarePtr); +#endif + + + RedirectionEntry.ra.Destination &= ~ApicIDBit; + + if (LastDetach) { + RedirectionEntry.ra.Mask |= APIC_INTR_MASKED; + } + + WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress, + RedirectionEntry.rb.dword1); + + WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress + 1, + RedirectionEntry.rb.dword2); +} + +// +// The interrupt handler run by the boot processor to process requests +// from other processors to change the I/O APIC redirection entries. In +// Cbus1, only the boot processor can reach the EISA I/O APIC, and thus, +// requests from other processors are funnelled through to here. +// +// this function is running whilst cli'd, and some additional processor +// is holding the CbusVectorLock. +// +VOID +CbusApicRedirectionInterrupt() +{ + ULONG ApicBusNumber; + ULONG ParticipatingProcessors; + ULONG RedirectionAddress; + REDIRECTION_T RedirectionEntry; + PREDIR_PORT_T CurrentPort = CbusApicRedirPort; + PREDIR_PORT_T FinalPort = &CbusApicRedirPort[CbusBootedProcessors]; +#if DBG + ULONG OrigStatus; + ULONG Processor; + + Processor = (ULONG) KeGetPcr()->HalReserved[PCR_PROCESSOR]; + ASSERT(Processor == 0); +#endif + + // + // get the base of APIC space, so we can then access + // the addr of hardware interrupt command register below + // + + for ( ; CurrentPort < FinalPort; CurrentPort++) { + + // + // first check for an entry that needs servicing. + // when one is found, load up the requestor's APIC ID + // and the I/O address of the redirection table to modify. + // immediately bump to the destination register (high word) + // of the redirection table entry since that must be modified + // first. then capture the current value. + // + + if ((CurrentPort->Status & REDIR_ACTIVE_REQUEST) == 0) + continue; + + RedirectionAddress = CurrentPort->RedirectionAddress; + ApicBusNumber = CurrentPort->BusNumber; + + // + // now that we have a valid request, see whether it was a + // DISABLE or ENABLE, and if it was the LAST detach or + // FIRST attach. take action appropriately... + // + + ParticipatingProcessors = READ_IOAPIC_ULONG(ApicBusNumber, + RedirectionAddress + 1); + + if (CurrentPort->Status & REDIR_ENABLE_REQUEST) { + + WRITE_IOAPIC_ULONG(ApicBusNumber, + RedirectionAddress + 1, + ParticipatingProcessors|CurrentPort->ApicID); + + if (CurrentPort->Status&REDIR_FIRSTATTACH_REQUEST) { + WRITE_IOAPIC_ULONG(ApicBusNumber, + RedirectionAddress, + CurrentPort->RedirectionCommand); + } + } + else { + ASSERT(CurrentPort->Status & REDIR_DISABLE_REQUEST); + + WRITE_IOAPIC_ULONG(ApicBusNumber, + RedirectionAddress + 1, + ParticipatingProcessors & + ~CurrentPort->ApicID); + + // + // Remove this processor's bit field number from + // the interrupt participation list. If this is + // the last processor to detach, mask off the + // interrupt at the source as well, so no one will + // need to arbitrate for it should it get asserted + // later. + // + + if (CurrentPort->Status & REDIR_LASTDETACH_REQUEST) { + RedirectionEntry.rb.dword1 = + READ_IOAPIC_ULONG(ApicBusNumber, + RedirectionAddress); + + RedirectionEntry.ra.Mask |= APIC_INTR_MASKED; + + WRITE_IOAPIC_ULONG(ApicBusNumber, + RedirectionAddress, + RedirectionEntry.rb.dword1); + } + } +#if DBG + OrigStatus = CurrentPort->Status; +#endif + + CurrentPort->Status = 0; +#ifdef LANDY_DBG + DbgPrint("Boot processor touching APIC at intr time, apic bus %d, redir %x, status=%x, callerid=%x, redircmd=%x, prevpart=%x\n", + ApicBusNumber, + RedirectionAddress, + OrigStatus, + CurrentPort->ApicID, + CurrentPort->RedirectionCommand, + ParticipatingProcessors); +#endif + + } +} + +PVOID +CbusApicLinkVector( +IN PBUS_HANDLER Bus, +IN ULONG Vector, +IN ULONG Irqline +) +/*++ + +Routine Description: + + "Link" a given vector to the passed BusNumber/irqline, returning + a "handle" that can be used to reference it later for operations + that need to access the hardware (ie: Enable & DisableInterrupt). + +Arguments: + + Vector - Supplies the system interrupt vector corresponding to the + specified BusNumber/Irqline. + + Irqline - Supplies the IRQ line of the specified interrupt + +Return Value: + + A hardware-specific pointer (actually a redirection entry address) + that is interpreted only by the Cbus1 backend. + +--*/ +{ + UNREFERENCED_PARAMETER( Bus ); + + // + // Set up the EOI address now + // + if (Vector != (ULONG)-1) { + CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector); + } + + // + // Warning - there is built-in knowledge of this math in + // Cbus1EnableDeviceInterrupt(). Check there before making + // changes here. This was not the best way to do things, + // but this bug wasn't discovered until just before release. + // + return (PVOID)(IO_APIC_REDIRLO + 2 * Irqline); +} diff --git a/private/ntos/nthals/halcbus/i386/cbdetect.c b/private/ntos/nthals/halcbus/i386/cbdetect.c new file mode 100644 index 000000000..3ad224b47 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbdetect.c @@ -0,0 +1,545 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbdetect.c + +Abstract: + + This module detects the presence of a Corollary architecture machine + which can utilize the Corollary-developed HAL for Windows NT. Currently, + the picture looks as follows: + + C-bus I original: uses standard Windows NT HAL + C-bus I XM: uses standard Windows NT HAL + + C-bus I Symmetric XM: uses Corollary-developed Windows NT HAL + C-bus II: uses Corollary-developed Windows NT HAL + + Thus, we return 0 for the first two cases, and non-zero for the last two. + +Author: + + Landy Wang (landy@corollary.com) 05-Oct-1992 + +Environment: + + Kernel mode only. This module must compile standalone, + and thus, any needed #defines have been copied directly + into this module. + +--*/ + +#ifndef _NTOS_ +#include "nthal.h" +#endif + +PVOID +HalpMapPhysicalMemory( + IN PVOID PhysicalAddress, + IN ULONG NumberPages + ); + + +// +// address of configuration passed +// +#define RRD_RAM 0xE0000 + +// +// extended structures passed by RRD ROMs to various kernels/HALs +// for the Corollary smp architectures +// +// layout of information passed to the kernels/HALs: +// The exact format of the configuration structures is hard +// coded in info.s (rrd). The layout is designed such that +// the ROM version need not be in sync with the kernel/HAL version. +// +// checkword: ULONG +// - extended configuration list must be terminated +// with EXT_CFG_END (0) +// length: ULONG +// - length is for structure body only; does not include +// either the checkword or length word +// +// structure body: format determined by checkword +// +// + +typedef struct _ext_cfg_header { + + ULONG ext_cfg_checkword; + ULONG ext_cfg_length; + +} EXT_CFG_HEADER_T, *PEXT_CFG_HEADER; + +// +// slot parameter structure (overrides any matching previous entry, +// but is usually used in conjunction with the ext_cfg_override) +// in processor_configuration or ext_memory_board. +// +// checkword is EXT_ID_INFO +// +// each structure is 16 bytes wide, and any number +// of these structures can be presented by the ROM. +// the kernel/HAL will keep reading them until either: +// +// a) an entry with id == 0x7f (this is treated as the list delimiter) +// OR +// b) the kernel (or HAL)'s internal tables fill up. at which point, only +// the entries read thus far will be used and the rest ignored. +// +#define EXT_ID_INFO 0x01badcab +typedef struct _ext_id_info { + + ULONG id:7; + ULONG pm:1; + ULONG proc_type:4; + ULONG proc_attr:4; + ULONG io_function:8; + ULONG io_attr:8; + ULONG pel_start; + ULONG pel_size; + + ULONG pel_features; + + ULONG io_start; + ULONG io_size; + +} EXT_ID_INFO_T, *PEXT_ID_INFO; + +#define LAST_EXT_ID 0x7f // delimit the extended ID list + +// +// configuration parameter override structure +// +// checkword is EXT_CFG_OVERRIDE. +// can be any length up to the kernel/HAL limit. this +// is a SYSTEMWIDE configuration override structure. +// +#define EXT_CFG_OVERRIDE 0xdeedcafe + +typedef struct _ext_cfg_override { + ULONG baseram; + ULONG memory_ceiling; + ULONG resetvec; + // + // cbusio is the base of global C-bus I/O space. + // + ULONG cbusio; + + UCHAR bootid; + UCHAR useholes; + UCHAR rrdarb; + UCHAR nonstdecc; + ULONG smp_creset; + ULONG smp_creset_val; + ULONG smp_sreset; + + ULONG smp_sreset_val; + ULONG smp_contend; + ULONG smp_contend_val; + ULONG smp_setida; + + ULONG smp_setida_val; + ULONG smp_cswi; + ULONG smp_cswi_val; + ULONG smp_sswi; + + ULONG smp_sswi_val; + ULONG smp_cnmi; + ULONG smp_cnmi_val; + ULONG smp_snmi; + + ULONG smp_snmi_val; + ULONG smp_sled; + ULONG smp_sled_val; + ULONG smp_cled; + + ULONG smp_cled_val; + + ULONG machine_type; + ULONG supported_environments; + ULONG broadcast_id; + +} EXT_CFG_OVERRIDE_T, *PEXT_CFG_OVERRIDE; + +#define EXT_CFG_END 0 + +#define MAX_CBUS_ELEMENTS 32 // max number of processors + I/O + +// +// Bit fields of pel_features if pm indicates it's a processor +// +#define ELEMENT_SIO 0x00001 // SIO present +#define ELEMENT_SCSI 0x00002 // SCSI present +#define ELEMENT_IOBUS 0x00004 // IO bus is accessible +#define ELEMENT_BRIDGE 0x00008 // IO bus Bridge +#define ELEMENT_HAS_8259 0x00010 // local 8259s present +#define ELEMENT_HAS_CBC 0x00020 // local Corollary CBC +#define ELEMENT_HAS_APIC 0x00040 // local Intel APIC +#define ELEMENT_RRD_RESERVED 0x20000 // Old RRDs used this + + +// +// Bit fields of machine types +// +#define MACHINE_CBUS1 0x1 // Original C-bus 1 +#define MACHINE_CBUS1_XM 0x2 // XM C-bus 1 +#define MACHINE_CBUS2 0x4 // C-bus 2 + +// +// Bit fields of supported environment types - each bit signifies that +// the specified operating system release is supported in full multiprocessor +// mode. Note that since the Cbus2 hardware changed, and initial hardware +// wasn't available until Q2 1994, Cbus2 RRDs will _NEVER_ set the +// 0x4 bit, and will instead set the 0x10 bit. This will have the effect +// of Cbus2 being supported in MP mode by NT release 3.5 and up. Cbus1 +// will be supported in MP mode by all NT releases (3.1 and up). +// +#define SCO_UNIX 0x01 +#define USL_UNIX 0x02 +#define WINDOWS_NT 0x04 // release 3.1 and up (July 1993) +#define NOVELL 0x08 +#define OS2 0x10 +#define WINDOWS_NT_R2 0x20 // release 3.5 and up (June 1994) + +extern ULONG CorollaryHalNeeded(PEXT_CFG_OVERRIDE, PEXT_ID_INFO, + PBOOLEAN); + + +PUCHAR +CbusFindString ( +IN PUCHAR Str, +IN PUCHAR StartAddr, +IN LONG Len +) + +/*++ + +Routine Description: + + Searches a given virtual address for the specified string + up to the specified length. + +Arguments: + + Str - Supplies a pointer to the string + + StartAddr - Supplies a pointer to memory to be searched + + Len - Maximum length for the search + +Return Value: + + Pointer to the string if found, 0 if not. + +--*/ + +{ + LONG Index, n; + + for (n = 0; Str[n]; ++n) + ; + + if (--n < 0) { + return StartAddr; + } + + for (Len -= n; Len > 0; --Len, ++StartAddr) { + if ((StartAddr[0] == Str[0]) && (StartAddr[n] == Str[n])) { + for (Index = 1; Index < n; ++Index) + if (StartAddr[Index] != Str[Index]) + break; + if (Index >= n) { + return StartAddr; + } + } + } + + return (PUCHAR)0; +} + + +// +// for robustness, we check for the following before concluding that +// we are indeed a Corollary C-bus I or C-bus II licensee: +// +// a) Corollary C-bus II string in the BIOS ROM 64K area (0x000F0000) +// b) Corollary C-bus II string in the RRD RAM/ROM 64K area (0xFFFE0000) +// c) 2 Corollary extended configuration tables +// in the RRD RAM/ROM 64K area (0xFFFE0000) +// +// if any of the above fail, we assume we are not a Corollary +// C-bus I or C-bus II box, and revert to normal uniprocessor +// behavior. +// + +static ULONG RRDextsignature[] = { 0xfeedbeef, 0 }; + +static CHAR crllry_owns[] = "Copyright(C) Corollary, Inc. 1991. All Rights Reserved"; + +ULONG +DetectCbusII( + OUT PBOOLEAN IsConfiguredMp +) +/*++ + +Routine Description: + Determine which Corollary platform, if any, is present. + +Arguments: + none. + +Return Value: + + Return TRUE to indicate the machine is a Corollary MP machine + which requires the Corollary Windows NT HAL for full performance. + + We only set IsConfiguredMp if we find more than 1 processor, + since it is only then that we want to incur the overhead of the + fully-built multiprocessor kernel. note that as long as we return + TRUE, our HAL will be used, so we will still get our improved + interrupt controller, ECC, additional memory, etc, etc. + + All other cases will return FALSE, and clear IsConfiguredMp as well. + +--*/ +{ + PEXT_CFG_HEADER p; + PEXT_ID_INFO Idp = (PEXT_ID_INFO)0; + PEXT_CFG_OVERRIDE CbusGlobal = (PEXT_CFG_OVERRIDE)0; + ULONG EntryLength; + PUCHAR Bios; + + // + // assume it's not a Corollary MP machine (requiring the + // Corollary Windows NT HAL for full performance) + // unless we detect otherwise below. + // + *IsConfiguredMp = FALSE; + + // + // map in the 64K (== 0x10 pages) of BIOS ROM @ 0xF0000 + // and scan it for our signature. Note that when this + // code runs, the entire low 16MB address space is + // identity mapped, so the HalpMapPhysicalMemory call + // just returns (virtual address == physical address). + // the upshot of this, is that we need not worry about + // exhausting PTEs and doing Remaps or Frees. however, + // this also means we cannot map in the 64K of + // RRD @ 0xFFFE0000 since HalpMapPhysicalMemory will fail. + // so skip this second check until HalpMapPhysicalMemory is fixed. + // + + Bios = (PUCHAR)HalpMapPhysicalMemory ((PVOID)0xF0000, 0x10); + + if (!CbusFindString((PUCHAR)"Corollary", Bios, (LONG)0x10000)) + return 0; + + // + // we'd like to map in the 64K (== 0x10 pages) of RRD @ 0xFFFE0000 and + // scan it for our signature. but we can't in the detect code, + // because HalpMapPhysicalMemory() can't deal with memory over + // the 16MB boundary. so leave it for the HAL to check this. + // +#if 0 + Bios = (PUCHAR)HalpMapPhysicalMemory ((PVOID)0xFFFE0000, 0x10); + + if (!CbusFindString((PULONG)"Corollary", Bios, (LONG)0x10000)) + return 0; +#endif + + // + // map in the 32K (== 8 pages) of RRD RAM information @ 0xE0000, + // + Bios = (PUCHAR)HalpMapPhysicalMemory ((PVOID)RRD_RAM, 8); + + if (!CbusFindString((PUCHAR)crllry_owns, Bios, (LONG)0x8000)) + return 0; + + // + // at this point, we are assured that it is indeed a + // Corollary architecture machine. search for our + // extended configuration tables, note we don't require + // (or even look for!) the existence of our earliest + // 'configuration' structure, ie: the 0xdeadbeef version. + // if there is no extended configuration structure, + // this must be an old rom. NO SUPPORT FOR THESE. + // + + p = (PEXT_CFG_HEADER)CbusFindString((PUCHAR)RRDextsignature, + Bios, (LONG)0x8000); + + // + // check for no extended configuration table: if it's not there, + // something is really wrong. we found our copyrights but not our + // machine? someone is copying us! no support for them! + // + + if (!p) + return 0; + + // + // Read in the 'extended ID information' table which, + // among other things, will give us the processor + // configuration. + // + // Multiple structures are strung together with a "checkword", + // "length", and "data" structure. The first null "checkword" + // entry marks the end of the extended configuration + // structure. + // + // we let the HAL deal with all the other extended configuration + // entries built by RRD. + // + + do { + EntryLength = p->ext_cfg_length; + + switch (p->ext_cfg_checkword) { + + case EXT_ID_INFO: + Idp = (PEXT_ID_INFO)(p + 1); + break; + + case EXT_CFG_OVERRIDE: + // + // reference up to the size of the structures + // we know about. if an rrd tries to pass us + // more than we know about, we ignore the + // overflow. underflow is interpreted as + // "this must be a pre-XM machine", and such + // machines must default to the standard Windows NT + // uniprocessor HAL. + // + + if (EntryLength < sizeof(EXT_CFG_OVERRIDE_T)) + return 0; + + CbusGlobal = (PEXT_CFG_OVERRIDE)(p + 1); + break; + + case EXT_CFG_END: + + // + // If ancient C-bus box, it's not supported in MP mode + // + if (!Idp || !CbusGlobal) + return 0; + + return CorollaryHalNeeded( + CbusGlobal, + Idp, + IsConfiguredMp); + + default: + // + // skip unused or unrecognized configuration entries + // note that here we only care about EXT_ID_INFO as far + // as presence checking goes, but the HAL will care + // about more than just that. + // + + break; + } + + // + // get past the header, add in the length and then + // we're at the next entry. + // + p = (PEXT_CFG_HEADER) ((PUCHAR)(p + 1) + EntryLength); + + } while (1); +} + + +// +// process the tables we have built to determine whether +// the machine is a Corollary MP machine which requires +// the Corollary Windows NT HAL for full performance. +// +// We always set IsConfiguredMp for this case even if +// this machine is configured as a uniprocessor, because it +// will still see improved performance from our HAL. +// + +ULONG +CorollaryHalNeeded( +IN PEXT_CFG_OVERRIDE CbusGlobal, +IN PEXT_ID_INFO p, +IN OUT PBOOLEAN IsConfiguredMp +) +{ + ULONG Index; + ULONG Processors = 0; + BOOLEAN MachineSupported = FALSE; + ULONG machine; + + machine = CbusGlobal->machine_type; + + // + // first check global platform data. if this indicates that + // this is an early C-bus machine (which will be + // supported in uniprocessor mode only by Windows NT), + // and thus, should use the default Windows NT HAL. + // + if (machine & MACHINE_CBUS2) { + + if ((CbusGlobal->supported_environments & WINDOWS_NT_R2) == 0) + return 0; + + MachineSupported = TRUE; + } + + if (machine & MACHINE_CBUS1_XM) { + + if ((CbusGlobal->supported_environments & (WINDOWS_NT|WINDOWS_NT_R2)) == 0) + return 0; + + MachineSupported = TRUE; + } + + if (MachineSupported == FALSE) + return 0; + + // + // then scan the table of IDs to make sure everything's ok + // + + for (Index = 0; Index < MAX_CBUS_ELEMENTS; Index++, p++) + { + + if (p->id == LAST_EXT_ID) { + break; + } + + // check only processor elements... + + if (p->pm == 0) + continue; + + // + // at least the base bridge must have a + // distributed interrupt chip (because + // any CPUs without them will be disabled). + // + + if (p->pel_features&(ELEMENT_HAS_APIC|ELEMENT_HAS_CBC)) { + Processors++; + } + else { + if (CbusGlobal->bootid == p->id) + return 0; + + } + } + + // This is an MP hal + + *IsConfiguredMp = TRUE; + + return 1; +} diff --git a/private/ntos/nthals/halcbus/i386/cbdriver.c b/private/ntos/nthals/halcbus/i386/cbdriver.c new file mode 100644 index 000000000..b321895b4 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbdriver.c @@ -0,0 +1,366 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbdrivers.c + +Abstract: + + This Windows NT module creates a software-viewable table of the valid + element spaces present in a Corollary C-bus II machine. Since + searching through non-existent memory space can cause NMIs, + it is highly recommended that all areas needing this information + consult the built software table rather than rescanning themselves. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "halp.h" +#include "cbus.h" // C-bus hardware architecture definitions +#include "cbus_nt.h" // C-bus NT-specific implementation #defines + +VOID +CbusIOPresent( + IN ULONG Id, + IN ULONG IoFunction, + IN ULONG IoAttribute, + IN ULONG IoStart, + IN ULONG IoSize, + IN PVOID Csr +); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,CbusIOPresent) +#endif + +#define MIN(a,b) (((a)>(b))?(b):(a)) + +// +// C-bus II half-card device driver interface: +// +// this scheme allows a single driver can have multiple +// cards (ie: elements) present, and the single driver can +// support all of them. +// +// to identify what I/O devices exist on the Cbus2 bus, software +// would need to disable NMI, and check all element spaces for +// valid element_type IDs (ie: NOT 0xff == BUS_TIMEOUT). since this +// work is HIGHLY Cbus2 specific (poking the interrupt control +// registers, turning off fault-enables, etc), this will be done +// by our ROM and passed to the HAL. +// +// the kernel will build a software table of all elements, +// CbusIoElements[]. software can scan this table at any time, +// looking for specific io_type IDs. no entries in this table +// should ever change once the table has been initially built. +// in this way, other pieces of NT can determine what's in the +// machine without worrying about generating NMIs, etc, etc. +// this software still needs to be integrated into the registry. +// +// it is assumed drivers MUST use the same intr map lines on all +// CBCs where his cards are attached. ie: it is ILLEGAL for a +// SCSI driver to use local irq4 for CPU1's adapter 6 and local +// irq5 for CPU2's adapter 6. +// +// it is legal for CPU3's localirq 5 can be a SCSI, whilst CPU4's +// localirq 5 can be an SIO. we distinguish them in the kernel +// because SIO and SCSI will have different io_type IDs. +// +// public I/O devices, control I/O, Prog I/O space of each +// element is to be mapped in by the drivers themselves. the +// HAL will not do this. the driver is provided the physical +// start and length of each element space via CbusIoElements[]. +// as described above. hence, no driver should scan global memory +// space searching for elements, which can cause NMIs, etc. +// + +typedef struct _cbus_io_elements { + + ULONG EntryLength; + ULONG ElementID; + ULONG ElementType; + ULONG IoAttribute; + ULONG AddressStart; // physical address in bytes + ULONG AddressLength; // length in bytes + ULONG CbcNumber; // BusNumber to drivers + PVOID Csr; // virtually mapped space + +} CBUS_IO_ELEMENTS_T, *PCBUS_IO_ELEMENTS; + +CBUS_IO_ELEMENTS_T CbusIoElements[MAX_ELEMENT_CSRS]; + +ULONG CbusIoElementIndex; + +ULONG CBCIndex; + +PVOID CbusCBCtoCSR( + IN ULONG CbcNumber +) +/*++ + +Routine Description: + + Convert the CBC number to a CSR pointer. + +Arguments: + + CbcNumber - Supplies the CBC number whose registers need to be accessed. + +Return Value: + + Pointer to the CSR referenced by the argument CBC number, 0 if the + argument was invalid. + +--*/ + +{ + PCBUS_IO_ELEMENTS p; + + if (CbcNumber >= CBCIndex) { + return (PVOID)0; + } + + for (p = CbusIoElements; p < &CbusIoElements[CbusIoElementIndex]; p++) { + if (p->CbcNumber == CbcNumber) { + return (PVOID)(p->Csr); + } + } + + return (PVOID)0; +} + + + +VOID +CbusIOPresent( + IN ULONG Id, + IN ULONG IoFunction, + IN ULONG IoAttribute, + IN ULONG IoStart, + IN ULONG IoSize, + IN PVOID Csr +) +/*++ + +Routine Description: + + Add the passed I/O entry to the table of Cbus I/O devices. + Note this table is for Cbus I/O devices only - EISA/ISA/MCA + devices are not part of this table. + + This table containing all the elements is constructed so we can + give Cbus hardware drivers (and the rest of NT) meaningful information. + +Arguments: + + From - Supplies a pointer to the RRD source table + + To - Supplies a pointer to the destination storage for the table + +Return Value: + + Number of valid table entries. + +--*/ + +{ + PCBUS_IO_ELEMENTS p, q; + + // + // If no room, can't register this I/O device. Should never happen. + // + + if (CbusIoElementIndex < MAX_ELEMENT_CSRS) { + + p = &CbusIoElements[CbusIoElementIndex]; + + // + // Storing the length field allows the HAL to add more + // information to an entry in the future, and still + // maintain compatibility with older driver binaries. + // + p->EntryLength = sizeof (CBUS_IO_ELEMENTS_T); + + // + // Process valid elements - these may or may + // not have a CPU on the card, but all can be accessed + // from any CPU in the system. memory cards and processors + // are not included here - this is for device drivers only. + // + p->ElementID = Id; + p->ElementType = IoFunction; + p->IoAttribute = IoAttribute; + p->AddressStart = IoStart; + p->AddressLength = IoSize; + p->Csr = Csr; + + // + // More than one I/O element may share a single CBC. + // This is handled right here. + // + for (q = CbusIoElements; q < p; q++) { + if (q->ElementID == Id) { + p->CbcNumber = q->CbcNumber; + break; + } + } + if (q == p) { + p->CbcNumber = CBCIndex++; + } + + CbusIoElementIndex++; + } +} + + +ULONG +Cbus2GetCbusData( + 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 Corollary Cbus data for a slot or address. + Drivers will be expected to call this function repeatedly with + incrementing BusNumbers & SlotNumbers, until this function returns 0, + indicating that the last slot has been read. Only one slot is + allowed per bus, with each I/O CBC representing a "bus". This is + necessary for interrupt vector allocation since slot numbers are + not passed into GetInterruptVector()... + + Each chunk of slot data should then be examined by the driver. + The driver may skip any chunk that doesn't belong to him. + Once a chunk is encountered that belongs to him, the CBCNumber + field must be treated as the "Cbus BusNumber" (it's really a CBC number, + not including the CBCs of the EISA bridges), to pass in when he asks for + an interrupt vector via HalGetInterruptVector(). + +Arguments: + + BusNumber - Indicates which bus. + + SlotNumber - Indicates which entry. + + 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. + +--*/ + +{ + ULONG BusNumber = RootHandler->BusNumber; + PUCHAR From; + ULONG MaxLength; + + // + // let our caller know when he's hit the end + // + + if (BusNumber >= CbusIoElementIndex) { + return 0; + } + + if (SlotNumber != 0) { + return 0; + } + + From = (PUCHAR)(CbusIoElements + BusNumber); + MaxLength = (CbusIoElementIndex - BusNumber) * sizeof (CBUS_IO_ELEMENTS_T); + + if (Offset >= MaxLength) { + return 0; + } + + if (Length+Offset > MaxLength) { + Length = MaxLength - Offset; + } + + RtlMoveMemory(Buffer, From+Offset, Length); + + return Length; +} + + +ULONG +Cbus2SetCbusData( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ) +/*++ + +Routine Description: + + The function sets the Corollary Cbus data for a slot or address. + +Arguments: + + BusNumber - Indicates which bus. + + SlotNumber - Indicates which entry. + + 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. + +--*/ + +{ + ULONG BusNumber = RootHandler->BusNumber; + PUCHAR To; + ULONG MaxLength; + + // + // don't allow writes beyond the end + // + if (BusNumber >= CbusIoElementIndex) { + return 0; + } + + if (SlotNumber != 0) { + return 0; + } + + To = (PUCHAR)(CbusIoElements + BusNumber); + MaxLength = (CbusIoElementIndex - BusNumber) * sizeof (CBUS_IO_ELEMENTS_T); + + if (Offset >= MaxLength) { + return 0; + } + + if (Length+Offset > MaxLength) { + Length = MaxLength - Offset; + } + + RtlMoveMemory(To+Offset, Buffer, Length); + + return Length; +} diff --git a/private/ntos/nthals/halcbus/i386/cbioacc.asm b/private/ntos/nthals/halcbus/i386/cbioacc.asm new file mode 100644 index 000000000..8d4e94313 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbioacc.asm @@ -0,0 +1,561 @@ + title "Cbus ioaccess" +;++ +; +; Copyright (c) 1992, 1993, 1994 Corollary Inc +; +; Module Name: +; +; cbioacc.asm +; +; Abstract: +; +; Procedures to correctly touch I/O registers. +; This is different from that of the standard PC HAL because +; we support multiple I/O buses as part of Cbus2. +; +; Author: +; +; Landy Wang (landy@corollary.com) 19 Mar 1994 +; +; Environment: +; +; User or Kernel, although privilege (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. Any address with +; the high bit on is assumed to be a HAL-memory-mapped equivalent +; of an I/O address, so it can just (and must!) be read/written +; directly. +; +;-- + + + +;++ +; +; UCHAR +; READ_PORT_UCHAR( +; PUCHAR Port +; ) +; +; Arguments: +; (esp+4) = Port +; +; Returns: +; Value in Port. +; +;-- +cPublicProc _READ_PORT_UCHAR ,1 +cPublicFpo 1, 0 + + mov edx,[esp+4] ; (dx) = Port + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + in al,dx + stdRET _READ_PORT_UCHAR + + align 4 +@@: + mov al, [edx] + 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 + + mov edx,[esp+4] ; (dx) = Port + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + in ax,dx + stdRET _READ_PORT_USHORT + + align 4 +@@: + mov ax, [edx] + 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 + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + in eax,dx + stdRET _READ_PORT_ULONG + + align 4 +@@: + mov eax, [edx] + 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 + + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + + rep insb + mov edi, eax + stdRET _READ_PORT_BUFFER_UCHAR + + align 4 +@@: + push eax ; save eax (really the orig edi) + + ; + ; this code to handle I/O to the extra busses should be written better + ; + align 4 +@@: + mov al, [edx] + mov [edi], al + inc edi + dec ecx + jnz @b + pop edi ; restore caller's edi + 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 (in 2-byte word units) +; +;-- +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 + + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + + rep insw + mov edi, eax + stdRET _READ_PORT_BUFFER_USHORT + + align 4 +@@: + push eax ; save eax (really the orig edi) + + ; + ; this code to handle I/O to the extra busses should be written better + ; + align 4 +@@: + mov ax, [edx] + mov [edi], ax + add edi, 2 + dec ecx + jnz @b + pop edi ; restore caller's edi + 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 (in 4-byte dword units) +; +;-- +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 + + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + + rep insd + mov edi, eax + stdRET _READ_PORT_BUFFER_ULONG + + align 4 +@@: + push eax ; save eax (really the orig edi) + + ; + ; this code to handle I/O to the extra busses should be written better + ; + align 4 +@@: + mov eax, [edx] + mov [edi], eax + add edi, 4 + dec ecx + jnz @b + pop edi ; restore caller's edi + 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 + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + out dx,al + stdRET _WRITE_PORT_UCHAR + + align 4 +@@: + mov [edx], 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 + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + out dx,ax + + stdRET _WRITE_PORT_USHORT + + align 4 +@@: + mov [edx], 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 + + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + + out dx,eax + stdRET _WRITE_PORT_ULONG + + align 4 +@@: + mov [edx], 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 + + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + + rep outsb + + mov esi,eax + stdRET _WRITE_PORT_BUFFER_UCHAR + + align 4 +@@: + push eax ; save eax (really the orig esi) + + ; + ; this code to handle I/O to the extra busses should be written better + ; + align 4 +@@: + mov al, byte ptr [esi] + mov byte ptr [edx], al + inc esi + dec ecx + jnz @b + pop esi ; restore caller's esi + 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 + + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + + rep outsw + mov esi,eax + stdRET _WRITE_PORT_BUFFER_USHORT + + align 4 +@@: + push eax ; save eax (really the orig esi) + + ; + ; this code to handle I/O to the extra busses should be written better + ; + align 4 +@@: + mov ax, word ptr [esi] + mov word ptr [edx], ax + add esi, 2 + dec ecx + jnz @b + pop esi ; restore caller's esi + 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 + + test edx, 080000000h ; high bit on? + jnz short @f ; yes, it is memory-mapped + + rep outsd + mov esi,eax + stdRET _WRITE_PORT_BUFFER_ULONG + + align 4 +@@: + push eax ; save eax (really the orig esi) + + ; + ; this code to handle I/O to the extra busses should be written better + ; + align 4 +@@: + mov eax, dword ptr [esi] + mov dword ptr [edx], eax + add esi, 4 + dec ecx + jnz @b + pop esi ; restore caller's esi + stdRET _WRITE_PORT_BUFFER_ULONG + +stdENDP _WRITE_PORT_BUFFER_ULONG + + +_TEXT$00 ends + + end diff --git a/private/ntos/nthals/halcbus/i386/cbmapint.c b/private/ntos/nthals/halcbus/i386/cbmapint.c new file mode 100644 index 000000000..7e65f67e6 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbmapint.c @@ -0,0 +1,579 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbmapint.c + +Abstract: + + This module implements the HalGetInterruptVector(), + HalDisableSystemInterrupt(), HalEnableSystemInterrupt(), + HalpInitializePICs() routines for the Corollary architectures + under Windows NT. + + As part of the move from Product1 to Product2, HalGetInterruptVector() + may no longer be called before Phase1 ! + + This is because we want HalGetInterruptVector() to eventually call our + routine (the parent handler) HalpGetSystemInterruptVector(). However, + the linkage is not established until the Phase 1 call to + HalpInitBusHandlers(). + +Author: + + Landy Wang (landy@corollary.com) 26-Apr-1992 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "halp.h" +#include "cbus_nt.h" // C-bus NT-specific implementation definitions + +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 PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +VOID +CbusPreparePhase0Interrupts( +IN ULONG, +IN ULONG, +IN PVOID +); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, HalpGetSystemInterruptVector) +#pragma alloc_text(PAGE, CbusPreparePhase0Interrupts) +#endif + + +// +// map the IRQLs to hardware interrupt vectors and vice versa +// +ULONG CbusIrqlToVector[HIGH_LEVEL + 1]; + +ULONG CbusVectorToIrql[MAXIMUM_IDTVECTOR + 1]; + +PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1]; + +// +// the CbusVectorTable[] structure is used to communicate information +// between HalGetInterruptVector() and HalEnableSystemInterrupt(). +// it would be nicer if it were built into an object instead of us +// having to carry it around, but that's life. +// +// ANY call to HalEnableSystemInterrupt() WITHOUT a preceding call +// to HalGetInterruptVector (by any processor) is ILLEGAL, unless +// it has been special cased and detected here. +// +// CbusVectorTable[] dimensioning must match the x86 IDT size as well. +// +typedef struct _vectortable_t { + ULONG CpusEnabled; // count of CPUs that have called + // HalEnableSystemInterrupt() + + PVOID hardware; // used by Cbus1 & Cbus2 backends + // to poke hardware + + USHORT BusNumber; // which bus # of a given class + + USHORT irqline; // only valid for actual I/O + // interrupts - not APC/DPC/IPI,etc. + +} VECTORTABLE_T, *PVECTORTABLE; + +VECTORTABLE_T CbusVectorTable[MAXIMUM_IDTVECTOR + 1]; + +// +// needed to synchronize allocation of new interrupt vectors as +// loading drivers request them. +// +KSPIN_LOCK CbusVectorLock; + +/*++ + +Routine Description: + + Called by each hardware backend from HalInitProcessor() at Phase 0 + by the boot cpu. All other cpus are still in reset. + + software(APC, DPC, wake) and IPI vectors have already been initialized + and enabled. + + all we're doing here is setting up some software structures for two + EISA interrupts (clock and profile) so they can be enabled later. + + most of the PIC initialization really happened via HalInitializeProcessor() + because the boot CPU needed to disable his interrupts right away. this + is because KiSystemStartup() calls KiInitializeKernel() which drops IRQL + to APC level. this happens LONG before HalpInitializePICs is ever called. + + As part of the move from Product1 to Product2, HalGetInterruptVector() + may no longer be called before Phase1 ! + + This is because we want HalGetInterruptVector() to eventually call our + routine (the parent handler) HalpGetSystemInterruptVector(). However, + the linkage is not established until the Phase 1 call to + HalpInitBusHandlers(). So this routine provides all the interrupt setup + that HalGetInterruptVector() does, but provides it at Phase0 init time. + +Arguments: + + SystemVector - the vector the interrupt will occur on. + + Opaque - an opaque hardware pointer that can only be interpreted by + the backend hardware portions of the HAL. + +Return Value: + + None. + +--*/ +VOID +CbusPreparePhase0Interrupts( +IN ULONG SystemVector, +IN ULONG Irqline, +IN PVOID Opaque +) +{ + CbusVectorTable[SystemVector].hardware = Opaque; + CbusVectorTable[SystemVector].irqline = (USHORT)Irqline; +} + +/*++ + +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. Since this operation is highly architecture + specific, we will defer the entire routine to our hardware backend handler. + + HalGetInterruptVector() must maintain a "vector to interrupt" + mapping so when the interrupt is enabled later via + HalEnableSystemInterrupt(), something intelligent can be done - + ie: which CBC's hardware interrupt maps to enable! + this applies both to EISA bridge CBCs and C-bus II half-card CBC's. + + note that HalEnableSystemInterrupt() will be CALLED by + EACH processor wishing to participate in the interrupt receipt. + +Arguments: + + BusHandler - the parent bus handler (ie: Internal) + + RootHandler - the root bus handler (ie: Cbus, Eisa, MCA, PCI, etc) + + 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 +HalpGetSystemInterruptVector( + IN PVOID Bhandler, + IN PVOID Rhandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ) + +{ + PBUS_HANDLER BusHandler = (PBUS_HANDLER)Bhandler; + PBUS_HANDLER RootHandler = (PBUS_HANDLER)Rhandler; + ULONG SystemVector; + PVECTORTABLE VectorObject; + extern ULONG HalpDefaultInterruptAffinity; + + SystemVector = (*CbusBackend->MapVector)( + RootHandler, + BusInterruptLevel, + BusInterruptVector, + Irql + ); + + if ( SystemVector > MAXIMUM_IDTVECTOR || + (HalpIDTUsage[SystemVector].Flags & IDTOwned) ) { + + // + // This is an illegal BusInterruptVector and cannot be connected. + // + return 0; + } + + if (SystemVector) { + + VectorObject = &CbusVectorTable[SystemVector]; + + if (RootHandler) + VectorObject->BusNumber = (USHORT)RootHandler->BusNumber; + else + VectorObject->BusNumber = 0; + + VectorObject->irqline = (USHORT)BusInterruptLevel; + + VectorObject->hardware = + + (*CbusBackend->LinkVector)( + RootHandler, + SystemVector, + BusInterruptLevel); + + // + // for the Corollary symmetric architectures, the + // interrupt affinity is always for all processors + // + + *Affinity = HalpDefaultInterruptAffinity; + ASSERT(HalpDefaultInterruptAffinity); + + } + + return SystemVector; +} + +/*++ + +Routine Description: + + Enables a system interrupt, written in C as this + is not expected to be a frequently used operation. + + this should not be called at interrupt level, as there + is not IRQL protection around CbusVectorLock + throughout the HAL. (there is IRQL protection here + against interrupt corruption, but not against lock + deadlocks). + + When enabling an interrupt on multiple CPUs, + _EACH_ CPU will run this routine - see IoConnectInterrupt + and KeConnectInterrupt and the thread affinity mechanism. + + virtually all interrupts are marked as LIG as far as the hardware + is concerned. This will be the "common" scenario, as most + drivers are fully multithreaded. Non multithreaded drivers + should only be calling this routine on ONE processor, and + thus, only that processor will participate in the "LIG"! + + The only interrupts NOT marked LIG are profile, timer and IPI. + Note that since there is no general kernel mechanism to notify + the HAL of this characteristic, the HAL must special case these, + which currently happens based on IRQL. So let NO OTHER INTERRUPTS + share the CLOCK2, IPI or PROFILE irqls!!! + + A conceptual difference between the APIC and the CBC is that when + an I/O APIC or CBC first gets a LIG interrupt from a device: + + - The CBC broadcasts it on the bus, and each processor that + is a member of the receiving group will contend for the interrupt. + The lowest priority processor will be the one to take it. + + - The I/O APIC knows what the set of the receiving group looks + like, and broadcasts it only to them, where the lowest priority + processor will be the one to take it. + + This difference (transparent in hardware) does influence software + setup and teardown of the interrupt which is why the Enable and + Disable routines are not common to both Cbus1 and Cbus2. + +Arguments: + + Vector - Supplies the vector of the interrupt to be enabled + + Irql - Supplies the interrupt level of the intr to be enabled + + InterruptMode - Supplies the interrupt mode of the interrupt + +Return Value: + + TRUE if enabled, FALSE if not. +--*/ + +BOOLEAN +HalEnableSystemInterrupt( + IN ULONG Vector, + IN KIRQL Irql, + IN KINTERRUPT_MODE InterruptMode + ) +{ + KIRQL OldIrql; + ULONG FirstAttach; + PVECTORTABLE VectorObject; + + ASSERT(Vector <= MAXIMUM_IDTVECTOR); + + UNREFERENCED_PARAMETER( InterruptMode ); + + // + // maintain a "vector-to-irql" mapping here for fast + // translation when accepting an interrupt. this is + // better than continually keeping fs:PcIrql updated, + // as it allows us to remove instructions from KfRaiseIrql + // and KfLowerIrql, the hot paths. + // + CbusVectorToIrql[Vector] = Irql; + + // + // first, try to enable it as a non-device interrupt - ie: + // without having to initialize an I/O bus to deliver it. + // if that succeeds (ie: for IPI or software interrupts like + // DPC or APC), then we're done. otherwise, go the + // long way. + // + if ((*CbusBackend->EnableNonDeviceInterrupt)(Vector) == TRUE) { + return TRUE; + } + + // + // it's a legitimate hardware device interrupt... + // + // for Cbus2, we need to poke the generating bridge's + // CBC registers (first enable only) to allow the I/O + // interrupt to look for a participating CBC to receive + // it, as well as this processor's CBC to notify the I/O + // CBC that this processor would like to participate + // in the interrupt arbitration. + // + // for Cbus1, we need to poke the I/O APIC of the generating + // bus, as well as this processor's local APIC. + // + // note that currently clock, profile & IPI are initialized via + // the KiSetHandlerAddresstoIDT() macro & HalEnableSystemInterrupt(). + // ie: HalGetInterruptVector() is NEVER called for them! + // + VectorObject = &CbusVectorTable[Vector]; + + // + // block all device interrupts (clock is the highest). + // do not block the HAL-private IPI which is used for + // Cbus1 (only) redirection table access, otherwise you + // will create a deadlock race condition. + // + OldIrql = KfRaiseIrql(CLOCK2_LEVEL); + + KiAcquireSpinLock(&CbusVectorLock); + + // + // check for "impossible" case... + // ie: where HalEnableSystemInterrupt has been called, + // but HalGetInterruptVector has NOT! with the exception + // of clock, profile, APC, DPC and IPI (which have hardcoded + // vectors), all drivers intending to receive interrupts must + // call HalGetInterruptVector to get a vector. + // + ASSERT (VectorObject->hardware); + + VectorObject->CpusEnabled += 1; + + FirstAttach = (VectorObject->CpusEnabled == 1 ? 1 : 0); + + (*CbusBackend->EnableDeviceInterrupt)( + Vector, + VectorObject->hardware, + FirstAttach, + VectorObject->BusNumber, + VectorObject->irqline); + + KiReleaseSpinLock(&CbusVectorLock); + + // + // unblock I/O interrupts + // + KfLowerIrql(OldIrql); + + return TRUE; +} + + +VOID +HalDisableSystemInterrupt( + IN ULONG Vector, + IN KIRQL Irql + ) +/*++ + + Routine Description: + + Disables a system interrupt, written in C as this + is not expected to be a frequently used operation. + should not be called at interrupt level, as there + is not IRQL protection around CbusVectorLock + throughout the HAL. (there is IRQL protection here + against interrupt corruption, but not against lock + deadlocks). + + This routine is only called when the interrupt may + actually be disabled - ie: it doesn't need to worry + about drivers sharing the interrupt line and one of + them disabling the line without the other's knowledge. + + Arguments: + + Vector - Supplies the vector of the interrupt to be disabled + + Irql - Supplies the interrupt level of the intr to be disabled + + Return Value: + + None. +--*/ + +{ + KIRQL OldIrql; + ULONG LastDetach = 0; + PVECTORTABLE VectorObject; + + UNREFERENCED_PARAMETER( Irql ); + + ASSERT(Vector < MAXIMUM_IDTVECTOR + 1); + + VectorObject = &CbusVectorTable[Vector]; + + // + // Block all device interrupts (clock is the highest). + // Do not block the HAL-private IPI which is used for + // Cbus1 (only) redirection table access since this + // would create a deadlock race condition. + // + OldIrql = KfRaiseIrql(CLOCK2_LEVEL); + + KiAcquireSpinLock(&CbusVectorLock); + + VectorObject->CpusEnabled -= 1; + + if (VectorObject->CpusEnabled == 0) { + LastDetach = 1; + } + + (*CbusBackend->DisableInterrupt)( + Vector, + (PVOID)VectorObject->hardware, + LastDetach, + VectorObject->BusNumber, + VectorObject->irqline); + + KiReleaseSpinLock(&CbusVectorLock); + + // + // unblock interrupts + // + KfLowerIrql(OldIrql); +} + + + +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; +} + diff --git a/private/ntos/nthals/halcbus/i386/cbswint.asm b/private/ntos/nthals/halcbus/i386/cbswint.asm new file mode 100644 index 000000000..56d633d72 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbswint.asm @@ -0,0 +1,444 @@ + title "Software Interrupts" +;++ +; +;Copyright (c) 1992, 1993, 1994 Corollary Inc +; +;Module Name: +; +; cbswint.asm +; +;Abstract: +; +; This module implements the HAL software interrupt routines +; for the MP Corollary implementation under Windows NT. +; +;Author: +; +; Landy Wang (landy@corollary.com) 26-Mar-1992 +; +;Environment: +; +; Kernel Mode +; +;Revision History: +; +;-- + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include i386\cbus.inc +include mac386.inc + + EXTRNP _HalBeginSystemInterrupt,3 + EXTRNP _HalEndSystemInterrupt,2 + + .list + +_TEXT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; FASTCALL +; HalRequestSoftwareInterrupt ( +; IN KIRQL RequestIrql +; ) +; +; Routine Description: +; +; This routine is used to issue a software interrupt to the +; calling processor. Since this is all done in hardware, the +; code to implement this is trivial. Our hardware supports +; sending the interrupt to lowest-in-group processors, which +; would be useful for a good number of DPCs, for example, but +; the kernel doesn't currently tell us which kinds of software +; interrupts need to go to the caller versus which can go to +; any processor. +; +; Arguments: +; +; (cl) = RequestIrql - Supplies the request IRQL value +; +; Return Value: +; +; None. +; +;-- + +cPublicFastCall HalRequestSoftwareInterrupt ,1 + + push ecx + call dword ptr [_CbusRequestSoftwareInterrupt] + fstRet HalRequestSoftwareInterrupt + +fstENDP HalRequestSoftwareInterrupt + +;++ +; +; 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. Since +; neither the APIC nor the CBC can clear an interrupt once it is sent, +; this optional function becomes a no-op in the Cbus HAL. +; +; Arguments: +; +; (cl) = RequestIrql - Supplies the request IRQL value +; +; Return Value: +; +; None. +; +;-- + +cPublicFastCall HalClearSoftwareInterrupt ,1 + 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. +; +; Note that the "software" interrupt has in fact been +; generated by software, but delivered by hardware - thus, no iret +; frame needs to be constructed here by software. +; +; Arguments: +; +; None +; Interrupt is disabled +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST hdpi_a, hdpi_t + +cPublicProc _HalpDispatchInterrupt ,0 +; +; Save machine state on trap frame +; + + ENTER_INTERRUPT hdpi_a, hdpi_t + + ; + ; The only thing we need to save here is the interrupted taskpri. + ; We must EOI the APIC immediately as there is no interrupt source, + ; and if we context switch and exit this thread, then we may never EOI! + ; the above macro will save all our registers, so we don't need to + ; below. Thus, the EOI serves as the HalEndSystemInterrupt. + ; + + mov eax, DPC_TASKPRI ; mark interrupting vec + CBUS_EOI eax, ecx ; destroy eax & ecx + + mov esi, dword ptr PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr + + push dword ptr [esi] ; save entry taskpri +if DBG + cmp dword ptr [esi], 0 ; old irql not zero? + je short irqlok + cmp dword ptr [esi], 01fh ; old irql not zero? + je short irqlok + int 3 +irqlok: +endif + + mov dword ptr [esi], DPC_TASKPRI ; set new h/w taskpri + sti ; and allow interrupts + + ; + ; Go do Dispatch Interrupt processing - we may context switch away from + ; this thread here, resume the idle thread and either much later (or + ; never) continue onward. so we must EOI _before_ dispatching with + ; this call. + ; + + stdCall _KiDispatchInterrupt + + ; + ; restore our original IRQL by programming it into the interrupt + ; controller since we will read it out of the interrupt controller + ; on the next KfRaiseIrql(). We must re-read the address of the + ; task priority register for the current processor before setting + ; it because we may be resuming a thread on a different processor + ; from the one that originally entered this routine. For the APIC, + ; this wouldn't matter because the task priority register is at the + ; same physical address for all processors. But for the CBC, each + ; task priority lies in global physical space (at different addresses). + ; since we are at DISPATCH_LEVEL here, we cannot be pre-empted as + ; we do this. + ; + +ifdef CBC_REV1 + pop eax ; get entry taskpri +if DBG + cmp eax, 0 ; old irql not zero? + je short irqlok2 + cmp eax, 01fh ; old irql not zero? + je short irqlok2 + int 3 +irqlok2: +endif + pushfd + cli + mov esi, dword ptr PCR[PcHal.PcrTaskpri] + mov dword ptr [esi], eax ; restore entry taskpri + popfd +else + mov esi, dword ptr PCR[PcHal.PcrTaskpri] + pop dword ptr [esi] ; restore entry taskpri +endif + + ; + ; Call this directly instead of through INTERRUPT_EXIT + ; because the HalEndSystemInterrupt has already been done, + ; and must only be done ONCE per interrupt. + ; + + cli + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without EOI + +stdENDP _HalpDispatchInterrupt + + 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. +; +; Note that the "software" interrupt has in fact been +; generated by software, but delivered by hardware - thus, no iret +; frame needs to be constructed here by software. +; +; Arguments: +; +; None +; Interrupt is Disabled +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST hapc_a, hapc_t + +cPublicProc _HalpApcInterrupt ,0 + +; +; Save machine state in trap frame +; + ENTER_INTERRUPT hapc_a, hapc_t + + ; The only thing we need to save here is the interrupted taskpri. + ; We must EOI the APIC immediately as there is no interrupt source, + ; and if we context switch and exit this thread, then we may never EOI! + ; the above macro will save all our registers, so we don't need to + ; below. Thus, the EOI serves as the HalEndSystemInterrupt. + + mov eax, APC_TASKPRI ; mark interrupting vec + CBUS_EOI eax, ecx ; destroy eax & ecx + + mov esi, dword ptr PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr + + push dword ptr [esi] ; save entry taskpri + +if DBG + cmp dword ptr [esi], 0 ; old irql not zero? + je short @f + int 3 +@@: +endif + + mov dword ptr [esi], APC_TASKPRI ; set new h/w taskpri + sti ; and allow interrupts + + ; call the APC delivery routine(previous mode,Null exception frame, + ; trap frame) + + + mov eax, [ebp]+TsSegCs ; get interrupted code's CS + and eax, MODE_MASK ; extract the mode + + stdCall _KiDeliverApc, <eax, 0, ebp> + + ; + ; restore our original IRQL by programming it into the interrupt + ; controller since we will read it out of the interrupt controller + ; on the next KfRaiseIrql(). We must re-read the address of the + ; task priority register for the current processor before setting + ; it because we may be resuming a thread on a different processor + ; from the one that originally entered this routine. For the APIC, + ; this wouldn't matter because the task priority register is at the + ; same physical address for all processors. But for the CBC, each + ; task priority lies in global physical space (at different addresses). + ; since we are at APC_LEVEL here, we must protect against a context + ; switch happening between reading the taskpri address and actually + ; setting its value, hence the cli... + ; + +ifdef CBC_REV1 + cli +endif +if DBG + cmp dword ptr [esp], 0 ; old irql not zero? + je short @f + int 3 +@@: +endif + mov esi, dword ptr PCR[PcHal.PcrTaskpri] + pop dword ptr [esi] ; restore entry taskpri + +if DBG + cmp dword ptr [esi], 0 ; old irql not zero? + je short @f + int 3 +@@: +endif + ; + ; Call this directly instead of through INTERRUPT_EXIT + ; because the HalEndSystemInterrupt has already been done, + ; and must only be done ONCE per interrupt. + ; + + cli + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi + +stdENDP _HalpApcInterrupt + + + page ,132 + subttl "HalRequestIpi" +;++ +; +; VOID +; HalRequestIpi( +; IN ULONG Mask +; ); +; +; Routine Description: +; +; Requests an interprocessor interrupt +; +; for Windows NT (and MPX 2.1), we use full distributed +; interrupt capability, and, thus, we will IGNORE the sswi address +; that RRD passes us and prioritize IPI as we see fit, given the +; other devices configured into the system. see the backend Cbus1 +; and Cbus2 handlers for more details. +; +; Arguments: +; +; Mask - Mask of processors to be interrupted +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _HalRequestIpi ,1 + + jmp dword ptr [_CbusRequestIPI] + +stdENDP _HalRequestIpi + +;++ +; +; VOID +; HalpIpiHandler ( +; ); +; +; Routine Description: +; +; This routine is entered as the result of an interrupt generated by inter +; processor communication. Its function is to call its handler. +; +; Arguments: +; +; None. +; Interrupt is dismissed +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST Hipi_a, Hipi_t + + align 4 + +cPublicProc _HalpIpiHandler ,0 + +; +; Save machine state in trap frame +; + + ENTER_INTERRUPT Hipi_a, Hipi_t ; (ebp) -> Trap frame + + ; + ; Save interrupting vector and previous IRQL. previous IRQL will + ; be filled in by our call to HalBeginSystemInterrupt, and the + ; stack space for both will be removed via INTERRUPT_EXIT's + ; call to HalEndSystemInterrupt. + ; + push dword ptr [_CbusIpiVector] + sub esp, 4 ; space for OldIrql + + ; + ; We now dismiss the interprocessor interrupt + ; (Irql, Vector, stack location of OldIrql) + + stdCall _HalBeginSystemInterrupt, <IPI_LEVEL, dword ptr [_CbusIpiVector], esp> + + ; Pass Null ExceptionFrame + ; Pass TrapFrame to Ipi service rtn + + stdCall _KiIpiServiceRoutine, <ebp, 0> + + ; + ; Do interrupt exit processing + ; + + INTERRUPT_EXIT ; will return to caller + +stdENDP _HalpIpiHandler + + +_TEXT ENDS + END diff --git a/private/ntos/nthals/halcbus/i386/cbsysint.asm b/private/ntos/nthals/halcbus/i386/cbsysint.asm new file mode 100644 index 000000000..27b492bb8 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbsysint.asm @@ -0,0 +1,590 @@ +;++ +; +;Copyright (C) 1992-1995 Corollary Inc +; +;Module Name: +; +; cbsysint.asm +; +;Abstract: +; +; This module implements some of the HAL routines to +; deal with interrupts for the MP Corollary implementations +; under Windows NT. The Cbus1 architecture uses the Intel +; APIC interrupt controller chip which is somewhat restricted +; in its capabilities. See cbus_nt.h for more details. +; The Cbus2 architecture can use the Corollary CBC or the Intel APIC, +; providing much greater interrupt granularity with the CBC. +; +; General HAL interrupt routines are coded in C for easy +; modification/portability; HAL interrupt routines which +; are frequently called are written here in assembly for +; greater speed. +; +;Environment: +; +; Kernel Mode +; +;-- + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\ix8259.inc +include i386\kimacro.inc +include cbus.inc + .list + +_TEXT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; KIRQL +; FASTCALL +; KfRaiseIrql ( +; IN KIRQL NewIrql +; ) +; +; Routine Description: +; +; This routine is used to raise IRQL to the specified value, +; and update the hardware task priority register. Since our +; underlying hardware supports "software" interrupts, the +; only need for KfRaiseIrql & KfLowerIrql to be different is +; because KfRaiseIrql must return the old irql. +; +; we cannot allow an interrupt between storing the old irql and +; raising irql to the requested level. this is only because some +; callers may specify a global store address for the previous irql. +; +; the sequence of instructions looks unusual in order to optimize +; memory fetches by separating the fetch from the usage wherever possible. +; +; Arguments: +; +; (cl) = NewIrql - the new irql to be raised to +; +; +; Return Value: +; +; OldIrql +; +;-- + +cPublicFastCall KfRaiseIrql,1 + + movzx ecx, cl ; get new irql value + +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr + + mov edx, [eax] ; get old taskpri val + + mov ecx, [_CbusIrqlToVector+4*ecx] ; convert new irql to taskpri + +if DBG + cmp ecx, edx ; new irql less than old? + jb short rfail +endif + + mov [eax], ecx ; set new hardware taskpri + + mov eax, [_CbusVectorToIrql+4*edx] ; convert old taskpri to irql + +ifdef CBC_REV1 + popfd +endif + + fstRET KfRaiseIrql + +if DBG +rfail: + push edx ; save old irql + push ecx ; save new irql + mov dword ptr [eax], 0 ; avoid recursive error + stdCall _KeBugCheck, <IRQL_NOT_GREATER_OR_EQUAL> ; no return +endif + +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 + + ; + ; Raise to DISPATCH_LEVEL + ; + +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr + + mov edx, [eax] ; get old taskpri val + + mov dword ptr [eax], DPC_TASKPRI ; set new hardware taskpri + +ifdef CBC_REV1 + popfd +endif + + mov eax, [_CbusVectorToIrql+4*edx] ; convert old taskpri to irql + stdRET _KeRaiseIrqlToDpcLevel +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 + + ; This should be optimized + + mov ecx, SYNCH_LEVEL + jmp @KfRaiseIrql + +stdENDP _KeRaiseIrqlToSynchLevel + + + + page ,132 + subttl "Lower irql" +;++ +; +; VOID +; FASTCALL +; KfLowerIrql ( +; IN KIRQL NewIrql +; ) +; +; Routine Description: +; +; This routine is used to lower IRQL to the specified value. +; Any pending software interrupts will be generated to the processor by +; hardware after the task priority/sti allows. +; +; the sequence of instructions looks unusual in order to optimize +; memory fetches by separating the fetch from the usage wherever possible. +; +; Arguments: +; +; (cl) = NewIrql - the new irql to be set. +; +; Return Value: +; +; None. +; +;-- + +cPublicFastCall KfLowerIrql, 1 + + movzx ecx, cl ; get new irql value + +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get hardware taskpri addr + + mov ecx, [_CbusIrqlToVector+4*ecx] ; convert irql to taskpri + +if DBG + cmp ecx, dword ptr [eax] ; is new greater than old? + ja lfail +endif + + mov [eax], ecx ; set new hardware taskpri + + ; + ; we must re-read the task priority register because this read + ; forces the write above to be flushed out of the write buffers. + ; otherwise the write above can get stuck and result in a pending + ; interrupt not being immediately delivered. in situations like + ; KeConnectInterrupt, the interrupt must be delivered in less than + ; 12 assembly instructions (the processor sends himself a rescheduling + ; DPC and has to execute it to switch to another CPU before continuing) + ; or corruption will result. because he thinks he has switched + ; processors and he really hasn't. and having the interrupt come in + ; after the 12 assembly instructions is _TOO LATE_!!! + ; + mov ecx, [eax] ; ensure it's lowered +ifdef CBC_REV1 + popfd +endif + + fstRET KfLowerIrql + +if DBG +lfail: + push ecx ; save new taskpri + push dword ptr [eax] ; save old taskpri + mov dword ptr [eax], 0ffh ; avoid recursive error + stdCall _KeBugCheck, <IRQL_NOT_LESS_OR_EQUAL> ; no return +endif + + fstRET KfLowerIrql +fstENDP KfLowerIrql + +;++ +; +; VOID +; HalEndSystemInterrupt ( +; IN KIRQL NewIrql, +; IN ULONG Vector +; ) +; +; Routine Description: +; +; This routine is used to lower IRQL to the specified value. +; Any pending software interrupts will be generated to the processor by +; hardware after the task priority/sti allows. +; +; Arguments: +; +; NewIrql - the new irql to be set. +; +; Vector - Vector number of the interrupt - (note this is different from +; the level we need to return to because multiple vectors may +; need to be blocked at given irql level). +; +; Note that esp+12 is the beginning of the interrupt/trap frame and upon +; entering this routine, interrupts are off. +; +; the sequence of instructions looks unusual in order to optimize +; memory fetches by separating the fetch from the usage wherever possible. +; +; Return Value: +; +; None. +; +;-- + +; equates for accessing arguments +; since eflags and ret addr are pushed into stack, all the arguments +; offset by 8 bytes +; + +HeiNewIrql equ [esp+4] +HeiNewVector equ [esp+8] + +cPublicProc _HalEndSystemInterrupt , 2 + + ; + ; issue the EOI for the interrupting vector now that the ISR has run + ; + mov eax, HeiNewVector ; get the interrupting vector + xor ecx, ecx ; faster than movzx + + CBUS_EOI eax, edx ; ack the interrupt controller + mov cl, byte ptr HeiNewIrql ; get new irql value + +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get hardware taskpri addr + + mov ecx, [_CbusIrqlToVector+4*ecx] ; convert new irql to taskpri + mov [eax], ecx ; set new hardware taskpri +ifdef CBC_REV1 + popfd +endif + + stdRET _HalEndSystemInterrupt + +stdENDP _HalEndSystemInterrupt + + +;++ +;BOOLEAN +;HalBeginSystemInterrupt( +; IN KIRQL Irql, +; IN CCHAR Vector, +; OUT PKIRQL OldIrql +; ) +; +; +; +;Routine Description: +; +; This routine is used to dismiss the specified Irql number. It is called +; before any interrupt service routine code is executed. +; +; N.B. This routine does NOT preserve EAX or EBX +; +;Arguments: +; +; Irql - Supplies the IRQL to raise to +; +; Vector - Supplies the vector of the interrupt to be dismissed +; +; OldIrql- Location to return OldIrql +; +; for 8259 architectures, vector == PIC_VECTBASE + irq line. +; for machines like ours (which can map arbitrary IRQ LINES to +; arbitrary interrupt VECTORS, unlike the 8259), vector is a +; completely independent value. notice that the higher numerical +; vector is the higher priority vector. When using the APIC, +; the EOI must be done after the s/w handler executes because +; EOI'ing the APIC will lower the APIC priority and allow interrupts. +; +; the sequence of instructions looks unusual in order to optimize +; memory fetches by separating the fetch from the usage wherever possible. +; note that "xor reg,reg & mov byte ptr" have been used instead of movzx +; since it's faster (on a Pentium). +; +; +;Return Value: +; +; FALSE - Interrupt is spurious and should be ignored - we +; have re-routed the spurious interrupt vector to a +; routine which just irets. thus, this routine will +; NEVER be called for a spurious interrupt and thus, +; will NEVER return FALSE. +; +; the CBC & APIC can receive spurious interrupts. +; barring I/O devices that float the line, spurious +; interrupts should only occur when: +; +; - the taskpri has been raised but is still in +; the CPU write buffer. +; +; - a lower priority interrupt comes in and the CBC/APIC +; signals an interrupt to the CPU. +; +; - the INTA cycles force the write buffers out, +; and the CBC/APIC realizes this lower-priority +; interrupt should really be blocked until later. +; +; - now the CBC/APIC will send a spurious interrupt to +; the CPU, and send the lower-priority interrupt +; later. +; +; 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 + + ; + ; Get the hardware task priority register address +ifdef CBC_REV1 + ; we're already cli'd, so don't worry about switching processors + ; after getting the task priority address +endif + ; + xor ecx, ecx ; faster than movzx + mov eax, PCR[PcHal.PcrTaskpri] + + ; + ; Capture the current hardware priority so we can return the old + ; IRQL to our caller. Then raise IRQL to requested level - this + ; is Cbus2 specific since the Cbus1 APIC automatically does this + ; in hardware. + ; + ; Crank hardware to new task priority level - we must use the + ; numerically lowest taskpri corresponding to the requested irql. + ; We cannot just use the vector passed in because multiple devices + ; may be configured at the same irql level, and have intertwined + ; dependencies. + ; + ; Note for the APIC, the hardware priority register + ; is automatically updated to block all the interrupts in + ; the bucket (and lower buckets) when this interrupt was + ; delivered. Also, that the priority is raised in an + ; APIC internal register, not the hardware task priority + ; register that is manipulated via KfRaiseIrql & KfLowerIrql. + ; + ; Raise the APIC priority explicitly here anyway in order to + ; maintain a common set of code with Cbus2. + ; + + mov cl, HbsiIrql ; get new irql value + + mov edx, [eax] ; get old taskpri val + + mov ecx, [_CbusIrqlToVector+4*ecx] ; irql --> taskpri + + mov [eax], ecx ; set new h/w taskpri + + ; + ; Done raising the new priority and getting the old priority, + ; now give our caller back the old priority in IRQL units. + ; + + mov ecx, HbsiOldIrql ; old irql savearea + mov edx, [_CbusVectorToIrql+4*edx] ; old taskpri --> irql + + mov byte ptr [ecx], dl + + sti + mov eax, 1 ; ALWAYS return TRUE + + stdRET _HalBeginSystemInterrupt + +stdENDP _HalBeginSystemInterrupt + +;++ +; +; KIRQL +; KeGetCurrentIrql (VOID) +; +; Routine Description: +; +; This routine returns to current IRQL. +; Note that for speed, we do NOT maintain a PCR +; version of the current irql (ie: movzx eax, fs:PcIrql). +; NOT doing this allows us to trim 3 instructions out of +; KfRaiseIrql & KfLowerIrql. Since KeGetCurrentIrql is +; rarely called, we take the hit of two extra instructions here +; instead. + +; +; Arguments: +; +; None. +; +; Return Value: +; +; The current IRQL. +; +;-- + +cPublicProc _KeGetCurrentIrql ,0 + +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr + mov eax, [eax] ; get taskpri value +ifdef CBC_REV1 + popfd +endif + + mov eax, [_CbusVectorToIrql+4*eax] ; convert to irql + + stdRET _KeGetCurrentIrql +stdENDP _KeGetCurrentIrql + +;++ +; +; VOID +; CbusDisable8259s (IN USHORT MASK) +; +; Routine Description: +; +; Called to disable 8259 input lines as we switch into full +; distributed interrupt chip mode. our distributed interrupt +; chip logic in the CBC will handle all interrupts from this +; point on. the only reason we have to leave irq0 enabled is +; because Intel's EISA chipset doesn't leave an external irq0 +; clock line for us to wire into the CBC. hence, we have wired +; it from the 8259 into the CBC, and must leave it enabled in +; the 8259 IMR. note that we will never allow the now passive +; 8259 to respond to a CPU INTA cycle, but we do need to see the +; interrupt ourselves in the CBC so we can drive an appropriate +; vector during the INTA. +; +; For the Cbus1 architecture, we mask off ALL the interrupts coming +; from the EISA chipset, since timers are generated by each local APIC. +; +; This is the ONLY place in the Corollary HAL (and all of NT!) +; where the 8259s are accessed. +; +; +; Arguments: +; +; Mask to put on the master and slave 8259. +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _CbusDisable8259s ,1 + mov ax, word ptr [esp+4] ; use specified 8259 mask + SET_8259_MASK + stdRET _CbusDisable8259s +stdENDP _CbusDisable8259s + + + page ,132 + subttl "HalpSpuriousInterrupt" +;++ +; +; VOID +; HalpSpuriousInterrupt(VOID) +; ); +; +; Routine Description: +; +; Entered directly from an interrupt gate to handle a spurious interrupt +; generated by the CBC or APIC interrupt controller. Just return, the +; real interrupt will be reposted by the hardware when our task priority +; drops later. +; +; Arguments: +; +; None. +; +; Return Value: +; +; None. +; +;-- +cPublicProc _HalpSpuriousInterrupt, 0 + + iretd ; IRET to clear the stack + +stdENDP _HalpSpuriousInterrupt + +_TEXT ENDS + END diff --git a/private/ntos/nthals/halcbus/i386/cbus.c b/private/ntos/nthals/halcbus/i386/cbus.c new file mode 100644 index 000000000..d581e2a84 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus.c @@ -0,0 +1,1281 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus.c + +Abstract: + + This module implements the initialization of the system dependent + functions that define the Hardware Architecture Layer (HAL) for the + MP Corollary machines under Windows NT. + + This includes the Corollary C-bus II machines which use Corollary's + CBC chips as well as Corollary C-bus I machines which use the Intel APIC. + Hardware dependencies of each C-bus backend are isolated in their + independent hardware modules. This module is completely hardware + independent. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "halp.h" +#include "cbus.h" // Cbus1 & Cbus2 max number of elements is here +#include "cbusrrd.h" // HAL <-> RRD interface definitions +#include "cbus_nt.h" // C-bus NT-specific implementation stuff +#include "cbusnls.h" + + +PVOID +HalpRemapVirtualAddress(IN PVOID, IN PVOID, IN BOOLEAN); + +BOOLEAN +CbusMPMachine(VOID); + +PUCHAR +CbusFindString ( +IN PUCHAR Str, +IN PUCHAR StartAddr, +IN LONG Len +); + +ULONG +CbusStringLength ( +IN PUCHAR Str +); + +ULONG +CbusReadExtIDs( +IN PEXT_ID_INFO From, +IN PEXT_ID_INFO To +); + +PVOID +CbusMappings( +IN ULONG Processor, +IN PEXT_ID_INFO Idp +); + +VOID +CbusMapMemoryRegisters( +IN PEXT_ID_INFO Idp +); + +VOID +CbusEstablishMaps( +IN PEXT_ID_INFO Table, +IN ULONG Count +); + +VOID +CbusReadRRD(VOID); + +VOID +CbusCheckBusRanges(VOID); + +VOID +CbusAddMemoryHoles(VOID); + +VOID +CbusInitializeOtherPciBus(VOID); + +VOID +HalInitializeProcessor( +IN ULONG Processor +); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, CbusStringLength) +#pragma alloc_text(INIT, CbusFindString) +#pragma alloc_text(INIT, CbusReadExtIDs) +#pragma alloc_text(INIT, CbusMappings) +#pragma alloc_text(INIT, CbusMapMemoryRegisters) +#pragma alloc_text(INIT, CbusEstablishMaps) +#pragma alloc_text(INIT, CbusReadRRD) +#pragma alloc_text(PAGE, HalInitializeProcessor) +#endif + +#define MIN(a,b) (((a)>(b))?(b):(a)) + +EXT_CFG_OVERRIDE_T CbusGlobal; + +ULONG CbusProcessors; +ULONG CbusProcessorMask; +ULONG CbusBootedProcessors; +ULONG CbusBootedProcessorsMask; + +ULONG CbusTemp; + +// +// 8254 spinlock. This must be acquired before touching the 8254 chip. +// + +ULONG Halp8254Lock; + +ULONG HalpDefaultInterruptAffinity; + +extern ULONG CbusVectorToIrql[MAXIMUM_IDTVECTOR + 1]; + +PULONG CbusTimeStamp; + +// +// For Cbus1, the CbusCSR[] & CbusBroadcastCSR really point at +// Cbus1 I/O space which can vary from platform to platform. +// +// For Cbus2, the CbusCSR[] & CbusBroadcastCSR really do point at +// the Cbus2 CSR for the particular element. +// +PVOID CbusBroadcastCSR; + +// +// Cbus information table for all elements (an element may not +// necessarily contain an x86 processor; ie: it may be a pure +// I/O element). +// +ELEMENT_T CbusCSR[MAX_CBUS_ELEMENTS]; + + +MEMORY_CARD_T CbusMemoryBoards[MAX_ELEMENT_CSRS]; +ULONG CbusMemoryBoardIndex; + +// +// hardcoded size for now - see cbus.inc for the register definition +// and layout of CbusRebootRegs[]. +// +ULONG CbusRebootRegs[8]; + +RRD_CONFIGURATION_T CbusJumpers; + +EXT_ID_INFO_T CbusExtIDTable[MAX_CBUS_ELEMENTS]; +ULONG CbusValidIDs; + +ULONG CbusVectorToHwmap[MAXIMUM_IDTVECTOR + 1]; + +// +// Declare the task priority system vectors which vary from APIC to CBC. +// About the only ones that remain constant are high, low and APC & DPC. +// This is primarily due to shortcomings and errata in the APIC. +// + +ULONG ProfileVector; +ULONG CbusIpiVector; +ULONG CbusClockVector; +ULONG CbusRedirVector; +ULONG CbusRebootVector; + +// +// Declare these two pointers here for speed - it eliminates an +// extra asm instruction each time they are called. +// + +VOID (*CbusRequestIPI)(IN ULONG); +VOID (*CbusRequestSoftwareInterrupt) ( IN KIRQL); +LARGE_INTEGER (*CbusQueryPerformanceCounter) ( IN OUT PLARGE_INTEGER); + +ADDRESS_USAGE HalpCbusMemoryHole = { + NULL, CmResourceTypeMemory, InternalUsage, + { + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 0, 0, + 0, 0, + 0, 0 + + } +}; + +// +// this structure differs from the one above in that it only contains +// memory ranges that we want reserved for the HAL and ensure that +// devices do not get dynamically assigned memory from these ranges. +// specifically, the table above will include more ranges that the +// BIOS E820 function will remove, but that need to remain available +// for resources on the secondary peer PCI bus for C-bus II. +// +ADDRESS_USAGE HalpCbusMemoryResource = { + NULL, CmResourceTypeMemory, InternalUsage, + { + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 0, 0, + 0, 0, + 0, 0 + + } +}; + +ULONG CbusMemoryHoleIndex; +ULONG CbusMemoryResourceIndex; + + +ULONG +CbusStringLength ( +IN PUCHAR Str +) + +/*++ + +Routine Description: + + Return the length of the input NULL-terminated Ansi string, including + the NULL terminator at the end. + +Arguments: + + Str - Supplies a pointer to the string + +Return Value: + + Length of the string in bytes + +--*/ + +{ + ULONG n; + + for (n = 0; Str[n]; ++n) + ; + + return ++n; +} + + +PUCHAR +CbusFindString ( +IN PUCHAR Str, +IN PUCHAR StartAddr, +IN LONG Len +) + +/*++ + +Routine Description: + + Searches a given virtual address for the specified string + up to the specified length. + +Arguments: + + Str - Supplies a pointer to the string + + StartAddr - Supplies a pointer to memory to be searched + + Len - Maximum length for the search + +Return Value: + + Pointer to the string if found, 0 if not. + +--*/ + +{ + LONG Index, n; + + for (n = 0; Str[n]; ++n) + ; + + if (--n < 0) { + return StartAddr; + } + + for (Len -= n; Len > 0; --Len, ++StartAddr) { + if ((StartAddr[0] == Str[0]) && (StartAddr[n] == Str[n])) { + for (Index = 1; Index < n; ++Index) + if (StartAddr[Index] != Str[Index]) + break; + if (Index >= n) { + return StartAddr; + } + } + } + + return (PUCHAR)0; +} + +ULONG +CbusReadExtIDs( +IN PEXT_ID_INFO From, +IN PEXT_ID_INFO To +) + +/*++ + +Routine Description: + + Read in the C-bus II extended id information table. + +Arguments: + + From - Supplies a pointer to the RRD source table + + To - Supplies a pointer to the destination storage for the table + +Return Value: + + Number of valid table entries. + +--*/ + +{ + ULONG Index = 0; + ULONG ValidEntries = 0; + + for ( ; Index < MAX_CBUS_ELEMENTS && From->id != LAST_EXT_ID; Index++) { + + // + // we cannot skip blank RRD entries + // + // if (From->pm == 0 && From->io_function == IOF_INVALID_ENTRY) + // continue; + + RtlMoveMemory((PVOID)To, (PVOID)From, sizeof(EXT_ID_INFO_T)); + + From++; + To++; + ValidEntries++; + } + + // + // WARNING: this is not necessarily the number of valid CPUs !!! + // + return ValidEntries; +} + +PVOID +CbusMappings( +IN ULONG Processor, +IN PEXT_ID_INFO Idp +) +/*++ + +Routine Description: + + Map a given processor's CSR space and save an idp pointer as well. + +Arguments: + + Processor - Supplies a logical processor number + + Idp - Supplies an RRD extended ID pointer for this processor element + +Return Value: + + Opaque pointer to this processor's CSR space.c + +--*/ +{ + // + // RRD specifies how much to map in per processor - this + // will usually be just 8K of the 64K CSR space for Cbus2. + // For Cbus1, RRD must give us a size which includes any + // processor-specific registers the HAL may access, + // generally indicated via the CbusGlobal structure. + // + CbusCSR[Processor].csr = HalpMapPhysicalMemoryWriteThrough ( + (PVOID)Idp->pel_start, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + Idp->pel_start, Idp->pel_size)); + + CbusCSR[Processor].idp = (PVOID)Idp; + + return CbusCSR[Processor].csr; +} + +VOID +CbusMapMemoryRegisters( +IN PEXT_ID_INFO Idp +) +/*++ + +Routine Description: + + Maps a given RRD entry into the HAL's memory board structures. + This is used later to determine ECC error addresses, etc. + +Arguments: + + Idp - Supplies a pointer to the RRD extended information structure entry + +Return Value: + + None. + +--*/ +{ + PMEMORY_CARD pm; + + pm = &CbusMemoryBoards[CbusMemoryBoardIndex]; + + pm->physical_start = Idp->pel_start; + pm->physical_size = Idp->pel_size; + pm->io_attr = (ULONG)Idp->io_attr; + + // + // map in the csr space for this memory card + // + pm->regmap = HalpMapPhysicalMemoryWriteThrough ( + (PVOID)Idp->io_start, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + Idp->io_start, Idp->io_size)); + + CbusMemoryBoardIndex++; +} + +VOID +CbusEstablishMaps( +IN PEXT_ID_INFO Table, +IN ULONG Count +) +/*++ + +Routine Description: + + Parse the given RRD extended ID configuration table, and construct + various HAL data structures accordingly. + +Arguments: + + Table - Supplies a pointer to the RRD extended information table + + Count - Supplies a count of the maximum number of entries. + +Return Value: + + None. + +--*/ +{ + ULONG Index, processor = 0; + ULONG HighVector; + ULONG Length; + PEXT_ID_INFO Idp = Table; + PUCHAR csr; + extern VOID CbusMemoryFree(ULONG, ULONG); + extern VOID CbusIOPresent(ULONG, ULONG, ULONG, ULONG, ULONG, PVOID); + extern ULONG IxProfileVector; + + for (Index = 0; Index < Count; Index++, Idp++) { + + // + // Map in the broadcast CSR. Note this is not a processor. + // + + if (Idp->id == CbusGlobal.broadcast_id) { + CbusBroadcastCSR = HalpMapPhysicalMemoryWriteThrough ( + (PVOID)Idp->pel_start, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + Idp->pel_start, Idp->pel_size)); + // + // Register the broadcast element's memory + // mapped I/O space + // + continue; + } + + + // + // Establish virtual maps for each processor + // + + if (Idp->pm) { + + if ((UCHAR)Idp->id == CbusGlobal.bootid) { + CbusMappings(0, Idp); + } + else { + + // + // We have an additional processor - set up + // his maps and put him in reset. We will + // boot him shortly. + // + + processor++; + + csr = (PUCHAR)CbusMappings(processor, Idp); + + csr += CbusGlobal.smp_sreset; + + *((PULONG)csr) = CbusGlobal.smp_sreset_val; + } + } + + // + // Establish virtual maps for each I/O and/or + // memory board. Note that I/O devices may or may + // not have an attached processor - a CPU is NOT required! + // memory, on the other hand, may NOT have a processor + // on the same board. + // + + switch (Idp->io_function) { + case IOF_INVALID_ENTRY: + case IOF_NO_IO: + break; + + case IOF_MEMORY: + // + // If not a processor, must be a memory card. + // Add this memory card to our list + // of memory cards in the machine. + // + + if (Idp->pm) + break; + + CbusMapMemoryRegisters(Idp); + + // + // Add this memory range to our list + // of additional memory to free later. + // + + CbusMemoryFree(Idp->pel_start, Idp->pel_size); + break; + + default: + // + // Add this I/O functionality to our table + // to make available to Cbus hardware drivers. + // Since I/O card interpretation of the RRD + // table is strictly up to the driver, do not + // try to register any of this element's space + // in the HAL's public consumption list, as we + // do for memory and processor cards. + // + + if (Idp->pel_features & ELEMENT_HAS_IO) { + + // + // If there was an attached processor, + // we already mapped in the CSR. If + // no attached processor, map in the + // CSR now. We'll need it later for + // interrupt vector enabling. + // + + if (Idp->pm == 0) { + csr = HalpMapPhysicalMemoryWriteThrough ( + (PVOID)Idp->pel_start, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + Idp->pel_start, Idp->pel_size)); + } + + CbusIOPresent( + (ULONG)Idp->id, + (ULONG)Idp->io_function, + (ULONG)Idp->io_attr, + Idp->pel_start, + Idp->pel_size, + (PVOID)csr ); + } + break; + } + } + + // + // Ensure that the memory subsystem does not use areas mapped out by + // e820 in determining the system memory ranges. + // + Length = 0xffffffff - CbusGlobal.cbusio; + AddMemoryHole(CbusGlobal.cbusio, Length + 1); + if (CbusBackend->AddMemoryHoles) + (*CbusBackend->AddMemoryHoles)(); + HalpRegisterAddressUsage (&HalpCbusMemoryResource); + + // + // Set total number of processors and their global mask + // + + CbusProcessors = processor + 1; + CbusProcessorMask = (1 << CbusProcessors) - 1; + + // + // Initialize the platform data - done only ONCE. + // Backends are expected to initialize the global Cbus + // spurious interrupt vector and the irqltovec[] table. + // We then pull the dispatch, wake and profile vectors + // from the table. + // + + (*CbusBackend->InitializePlatform)(); + + CbusRequestIPI = CbusBackend->HalRequestInterrupt; + CbusRequestSoftwareInterrupt = CbusBackend->HalRequestSoftwareInterrupt; + CbusQueryPerformanceCounter = CbusBackend->HalQueryPerformanceCounter; + + ProfileVector = CbusIrqlToVector[PROFILE_LEVEL]; + CbusClockVector = CbusIrqlToVector[CLOCK2_LEVEL]; + CbusIpiVector = CbusIrqlToVector[IPI_LEVEL]; + + HighVector = CbusIrqlToVector[HIGH_LEVEL]; + CbusVectorToIrql[HighVector] = HIGH_LEVEL; + + // + // Initialize the standard IxProfileVector so that we can + // reuse the standard profile code. + // + + IxProfileVector = ProfileVector; +} + +VOID +HalpResetAllProcessors(VOID) +/*++ + +Routine Description: + + Called to put all the other processors in reset prior to reboot for + the Corollary architectures. Highly architecture specific. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + ULONG Processor; + + Processor = KeGetPcr()->HalReserved[PCR_PROCESSOR]; + + (*CbusBackend->ResetAllOtherProcessors)(Processor); +} + +UCHAR ObsoleteMachine[] = MSG_OBSOLETE; + +VOID +FatalError( +IN PUCHAR ErrorString +) +/*++ + +Routine Description: + + Called to halt the HAL due to a fatal error, printing out a + string describing the cause of the failure. + +Arguments: + + ErrorString - Supplies a pointer to failure message + +Return Value: + + None. + +--*/ + +{ + + HalDisplayString(ErrorString); + HalDisplayString(MSG_HALT); + + while (1) + ; +} + +static ULONG RRDextsignature[] = { 0xfeedbeef, 0 }; + +static ULONG RRDsignature[] = { 0xdeadbeef, 0 }; + +static UCHAR CorollaryOwns[] = "Copyright(C) Corollary, Inc. 1991. All Rights Reserved"; + +VOID +CbusReadRRD(VOID) + +/*++ + + Routine Description: + + For robustness, we check for the following before concluding that + we are indeed a Corollary C-bus I or C-bus II licensee supported + in multiprocessor mode under this HAL: + + a) Corollary string in the BIOS ROM 64K area (0x000F0000) + b) Corollary string in the RRD RAM/ROM 64K area (0xFFFE0000) + c) 2 Corollary extended configuration tables + in the RRD RAM/ROM 64K area (0xFFFE0000) + + If any of the above checks fail, it is assumed that this machine + is either a non-Corollary machine or an early Corollary machine + not supported by this HAL. Both of these types of machines are, + however, supported (in uniprocessor mode) by the standard + uniprocessor HAL. + + If the above checks succeed, then we proceed to fill in various + configuration structures for later use. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + ULONG Index; + PEXT_CFG_HEADER p; + PVOID s; + ULONG OverrideLength = 0, EntryLength; + PUCHAR Bios; + + // + // Map in the 64K (== 0x10 pages) of BIOS ROM @ 0xF0000 + // and scan it for our signature... + // + + Bios = (PUCHAR)HalpMapPhysicalMemory ((PVOID)0xF0000, 0x10); + + if (!CbusFindString((PUCHAR)"Corollary", Bios, (LONG)0x10000)) + KeBugCheck (MISMATCHED_HAL); + + // + // Map in the 64K (== 0x10 pages) of RRD @ 0xFFFE0000 and + // scan it for our signature. (Note we are taking advantage + // of the fact that the lengths are the same, thus reusing + // the above PTEs, as opposed to allocating new ones). + // + + for (Index = 0; Index < 0x10; Index++) { + HalpRemapVirtualAddress( + Bios + (Index << PAGE_SHIFT), + (PVOID)(0xFFFE0000 + (Index << PAGE_SHIFT)), + FALSE); + } + + if (!CbusFindString((PUCHAR)"Corollary", Bios, (LONG)0x10000)) + KeBugCheck (MISMATCHED_HAL); + + // + // Map in the 32K (== 8 pages) of RRD RAM information @ 0xE0000, + // again reusing previously gained PTEs. Note we are only reusing + // the low half this time. + // + + for (Index = 0; Index < 8; Index++) { + HalpRemapVirtualAddress( + Bios + (Index << PAGE_SHIFT), + (PVOID)(RRD_RAM + (Index << PAGE_SHIFT)), + FALSE); + } + + if (!CbusFindString((PUCHAR)CorollaryOwns, Bios, (LONG)0x8000)) + FatalError(ObsoleteMachine); + + // + // At this point, we are assured that it is indeed a + // Corollary architecture machine. Search for our + // extended configuration tables, note we still search for + // the existence of our earliest 'configuration' structure, + // ie: the 0xdeadbeef version. This is not to find out where + // the memory is, but to find out which Cbus1 megabytes have been + // 'jumpered' so that I/O cards can use the RAM address(es) for + // their own dual-ported RAM buffers. This early configuration + // structure will not be present in Cbus2. + // + // If there is no extended configuration structure, + // this must be an old rom. NO SUPPORT FOR THESE. + // + + s = (PVOID)CbusFindString((PUCHAR)RRDsignature, Bios, + (LONG)0x8000); + + if (s) { + RtlMoveMemory((PVOID)&CbusJumpers, (PVOID)s, JUMPER_SIZE); + } +#if DBG + else { + // + // RRD configuration is not expected on Cbus2, but is for Cbus1 + // + HalDisplayString("HAL: No RRD ROM configuration table\n"); + } +#endif + + // + // Now go for the extended configuration structure which will tell + // us about memory, processors and I/O devices. + // + + p = (PEXT_CFG_HEADER)CbusFindString((PUCHAR)RRDextsignature, + Bios, (LONG)0x8000); + + if (!p) { +#if DBG + HalDisplayString("HAL: No extended configuration table\n"); +#endif + FatalError(ObsoleteMachine); + } + + // + // Read in the 'extended ID information' table which, + // among other things, will give us the processor + // configuration. + // + // Multiple structures are strung together with a "checkword", + // "length", and "data" structure. The first null "checkword" + // entry marks the end of the extended configuration + // structure. + // + // We are only actively reading two types of structures, and + // they MUST be in the following order, although not necessarily + // consecutive: + // + // - ext_id_info + // + // - ext_cfg_override + // + // We ignore all other extended configuration entries built + // by RRD - they are mainly for early UNIX kernels. + // + + do { + EntryLength = p->ext_cfg_length; + + switch (p->ext_cfg_checkword) { + + case EXT_ID_INFO: + + CbusValidIDs = CbusReadExtIDs((PEXT_ID_INFO)(p+1), + (PEXT_ID_INFO)CbusExtIDTable); + + break; + + case EXT_CFG_OVERRIDE: + // + // We just copy the size of the structures + // we know about. If an rrd tries to pass us + // more than we know about, we ignore the + // overflow. Underflow is interpreted as + // "this must be a pre-XM machine", and such + // machines must default to the standard Windows NT + // uniprocessor HAL. + // + + if (EntryLength < sizeof(EXT_CFG_OVERRIDE_T)) { + FatalError(MSG_RRD_ERROR); + } + + OverrideLength = MIN(sizeof(EXT_CFG_OVERRIDE_T), + EntryLength); + + RtlMoveMemory((PVOID)&CbusGlobal, + (PVOID)(p + 1), OverrideLength); + + break; + + case EXT_CFG_END: + + // + // If ancient C-bus box, it's not supported in MP mode + // + if (CbusValidIDs == 0 || OverrideLength == 0) { +#if DBG + HalDisplayString("HAL: Missing RRD tables\n"); +#endif + FatalError(ObsoleteMachine); + } + + if (CbusMPMachine() == FALSE) { +#if DBG + HalDisplayString("HAL: This Corollary machine is not supported under this HAL\n"); +#endif + FatalError(ObsoleteMachine); + } + + (*CbusBackend->ParseRRD)((PVOID)CbusExtIDTable, + &CbusValidIDs); + + + CbusEstablishMaps(CbusExtIDTable, CbusValidIDs); + + return; + + default: + // + // Skip unused or unrecognized configuration entries + // + + break; + } + + // + // Get past the header, add in the length and then + // we're at the next entry. + // + p = (PEXT_CFG_HEADER) ((PUCHAR)(p + 1) + EntryLength); + + } while (1); + + // never reached +} + +VOID +CbusCheckBusRanges(VOID) +/*++ + +Routine Description: + + Check all buses and determine SystemBase + for all ranges within all buses. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + if (CbusBackend->CheckBusRanges) { + (*CbusBackend->CheckBusRanges)(); + } +} + +VOID +CbusAddMemoryHoles(VOID) +/*++ + +Routine Description: + + Find the holes in the C-bus memory space that is not + useable for device allocation. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + if (CbusBackend->AddMemoryHoles) { + (*CbusBackend->AddMemoryHoles)(); + } +} + +VOID +CbusInitializeOtherPciBus(VOID) +/*++ + +Routine Description: + + Find and initialize other PCI system buses. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + if (CbusBackend->InitializeOtherPciBus) { + (*CbusBackend->InitializeOtherPciBus)(); + } +} + +VOID +HalpDisableAllInterrupts (VOID) +/*++ + +Routine Description: + This routine is called during a system crash. The Hal needs all + interrupts disabled. Interrupts will NOT be enabled upon leaving + this routine, nor is it allowed to turn them back on later. this + is a one-time thing, done as the system is coming down. + + Disables all incoming interrupts for the calling processor. + +Arguments: + + None. + +Return Value: + + None - all interrupts are masked off + +--*/ +{ + KfRaiseIrql(HIGH_LEVEL); +} + + +/*++ + +Routine Description: + + Called by each processor in turn, to initialize himself. + + - This is the earliest point at which the HAL gets control of + the system on each processor. + + - The boot CPU runs it early on in kernel startup. Much later, + each additional CPU will also run it shortly after they are + brought out of reset during Phase 1. + + - When called by the boot processor, this routine also reads in + the global RRD information which pertains to the entire system, + including all the processors. + + - Later, the boot cpu will run HalInitSystem --> HalpInitMP. + This all occurs at Phase 0. + + Much later, the Phase1 thread runs on the boot cpu, and calls + HalInitSystem --> HalpInitMP again, this time at Phase 1. + Then the boot cpu runs KeStartAllProcessors. this serially + invokes HalStartNextProcessor for each of our additional cpus + to start them running KiSystemStartup. + + As each additional processor runs KiSystemStartup, he then + runs HalInitializeProcessor. This is when we will enable the + additional processor's incoming IPIs. After that, he will proceed + to KiInitializeKernel & ExpInitializeExecutive, who then calls + HalInitSystem. Each additional processor is always running + at Phase 1, never Phase 0. + + The above dictates the actions of these routines: + + - HalInitializeProcessor will read in (ONCE only, ie: only the + boot cpu will do this) all of the element space and set up + _global_ maps for each processor's CSR. each CPU will map + his own _local_ CSR into his HAL pcr. each CPU will + enable his incoming IPI here, and disable all other interrupts, + including all of this CPU's half-card CBC I/O interrupts. + + - It would be nice to move some of the HalInitializeProcessor Phase0 + code to HalInitSystem Phase0, but we need full mappings, etc, + for entering the debugger from KiSystemStartup early on. + + - KeReadir/LowerIrq's must be available once this function + returns. (IPI's are only used once two or more processors are + available) + +Arguments: + + Processor - Supplies a logical processor number + +Return Value: + + None. + +--*/ +VOID +HalInitializeProcessor( +IN ULONG Processor +) +{ + extern VOID i486cacheon(VOID); + extern VOID HalpInitializeCoreIntrs(VOID); + extern VOID CbusDefaultStall(VOID); + extern VOID HalpIpiHandler( VOID ); + extern PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1]; + extern KSPIN_LOCK CbusVectorLock; + ULONG i; + ELEMENT_T csr; + PEXT_ID_INFO Idp; + extern KAFFINITY HalpActiveProcessors; + + if (Processor == 0) { + + // + // Find our signature and configuration information + // + CbusReadRRD(); + + // + // CbusVectorLock needs to be initialized before + // any CBC interrupt vectors are given out via + // HalGetInterruptVector(). + // + KeInitializeSpinLock(&CbusVectorLock); + + // + // Initialize the Eoi addresses to point at a known don't-care. + // Any interrupt that gets enabled later will get the correct + // EOI address filled in by the hardware backend interrupt + // enabling code. + // + for (i = 0 ; i <= MAXIMUM_IDTVECTOR; i++) { + CbusVectorToEoi[i] = &CbusTemp; + } + CbusTimeStamp = &CbusTemp; + } + + // + // Default stall execution to something reasonable + // until we initialize it later in HalInitSystem. + // + CbusDefaultStall(); + + // + // Enable this processor's internal cache - do this + // before stall execution is initialized in HalInitSystem. + // + csr = CbusCSR[Processor]; + + Idp = (PEXT_ID_INFO)csr.idp; + + // + // Enable the processor internal cache here... + // + if (Idp->proc_attr == PA_CACHE_ON) { + i486cacheon(); + } + + // + // Map this CPU's CSR stuff into his local address space for + // fast access. Also his logical # and bit position. + // + + (PVOID) KeGetPcr()->HalReserved[PCR_CSR] = CbusCSR[Processor].csr; + + (ULONG) KeGetPcr()->HalReserved[PCR_PROCESSOR] = Processor; + + (ULONG) KeGetPcr()->HalReserved[PCR_BIT] = (1 << Processor); + + (ULONG) KeGetPcr()->HalReserved[PCR_ALL_OTHERS] = + (CbusProcessorMask & ~(1 << Processor)); + + (PVOID) KeGetPcr()->HalReserved[PCR_LED_ON] = (PVOID) + ((PUCHAR)CbusCSR[Processor].csr + CbusGlobal.smp_sled); + + (PVOID) KeGetPcr()->HalReserved[PCR_LED_OFF] = (PVOID) + ((PUCHAR)CbusCSR[Processor].csr + CbusGlobal.smp_cled); + + // + // Since our architecture is completely symmetric, + // update affinity to contain each booted processor. + // + HalpDefaultInterruptAffinity |= (1 << Processor); + + // + // This parameter is returned by the HAL when the system asks + // for the HAL's configured resources list. + // + HalpActiveProcessors = HalpDefaultInterruptAffinity; + + CbusBootedProcessors += 1; + CbusBootedProcessorsMask = (1 << CbusBootedProcessors) - 1; + + // + // Initialize this processor's data - done ONCE by each processor. + // + // Typically, this processor's interrupt controller is initialized + // here. Also, if it's the first processor, any I/O interrupt + // controllers will also be initialized here, ie: EISA bridges or + // I/O APICs. + // + (*CbusBackend->InitializeCPU)(Processor); + + // + // This is where we actually enable IPI, APC, DPC and + // SPURIOUS interrupts. Device interrupts (like clock and + // profile) will not be enabled until HalInitSystem calls + // HalpInitializePICs later. Since we are still cli'd, no + // interrupt could actually bop us until KiSystemStartup + // calls KiInitializeKernel who drops IRQL. + // + HalpInitializeCoreIntrs(); +} + +#define CBUS1_NMI_MASK (PUCHAR)0x70 +#define CBUS1_IO_CHANNEL_CHECK (PUCHAR)0x61 + +VOID +CbusClearEISANMI(VOID) +/*++ + +Routine Description: + + This function clears the NMI on the EISA bus. Typically this + was generated by one of Corollary's "NMI cards", used for + debugging purposes. Our caller will have pointed us at the + correct bus bridge prior to calling us. note therefore we cannot + display anything because we may not be pointing at the default + display - if we want to display, we must map the bridge containing + the default video adapter! + +Arguments: + + None. + +Return Value: + + None. +--*/ +{ + UCHAR IoChannelCheck; + + WRITE_PORT_UCHAR(CBUS1_NMI_MASK, (UCHAR)0); + + IoChannelCheck = READ_PORT_UCHAR(CBUS1_IO_CHANNEL_CHECK); + WRITE_PORT_UCHAR(CBUS1_IO_CHANNEL_CHECK, + (UCHAR)((IoChannelCheck & 0xF) | 0x08)); + + IoChannelCheck = READ_PORT_UCHAR(CBUS1_IO_CHANNEL_CHECK); + WRITE_PORT_UCHAR(CBUS1_IO_CHANNEL_CHECK, + (UCHAR)(IoChannelCheck & 0x7)); + + // + // Since the NMI we are clearing was caused by pressing the button, + // which generated an EISA NMI (not a Cbus NMI), don't clear the + // NMI in Cbus space. + // + // COUTB(CbusCSR[Processor].csr, CbusGlobal.smp_cnmi, + // CbusGlobal.smp_cnmi_val); + // +} diff --git a/private/ntos/nthals/halcbus/i386/cbus.h b/private/ntos/nthals/halcbus/i386/cbus.h new file mode 100644 index 000000000..68ab7d425 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus.h @@ -0,0 +1,97 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus.h + +Abstract: + + Cbus architecture definitions for the Corollary C-bus I & II + multiprocessor HAL modules. The common hardware definitions + needed for the Windows NT HAL reside here. Hardware + architecture-specific definitions are in their respective modules. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ +#ifndef _CBUS_ +#define _CBUS_ +// +// used to read/write the current task priority. reads DO NOT +// have to be AND'ed with 0xff - this register has been +// guaranteed by both Corollary (for the CBC) and Intel +// (for the APIC) so that bits 8-31 will always read zero. +// (The Corollary guarantee is written, the Intel is verbal). +// +// note that this definition is being used both for the +// 64-bit CBC and the 32-bit APIC, even though the APIC +// really only has the low 32 bits. +// +// Task Priority ranges from a low of 0 (all interrupts unmasked) +// to a high of 0xFF (all interrupts masked) on both CBC and APIC. +// +typedef union _taskpri_t { + struct { + ULONG pri : 8; + ULONG zero : 24; + ULONG reserved1 : 32; + } ra; + struct { + ULONG LowDword; + ULONG HighDword; + } rb; +} TASKPRI_T, *PTASKPRI; + +// +// max number of C-bus II elements & processors. +// (processors == elements - broadcast element). +// + +#define MAX_ELEMENT_CSRS 15 +#define MAX_CBUS_ELEMENTS (MAX_ELEMENT_CSRS + 1) + +typedef struct _element_t { + PVOID csr; // opaque pointer to this CPU's CSR + PVOID idp; // opaque pointer to this CPU's RRD info entry +} ELEMENT_T, PELEMENT; + +extern ELEMENT_T CbusCSR[]; + +// +// list of memory boards in the system +// +typedef struct _memory_card_t { + + PULONG regmap; + ULONG io_attr; + ULONG physical_start; + ULONG physical_size; + +} MEMORY_CARD_T, *PMEMORY_CARD; + +extern MEMORY_CARD_T CbusMemoryBoards [MAX_ELEMENT_CSRS]; +extern ULONG CbusMemoryBoardIndex; + +#define PAGES_TO_BYTES(Page) (Page << PAGE_SHIFT) + +#define AddMemoryHole(start, length) \ + HalpCbusMemoryHole.Element[CbusMemoryHoleIndex].Start = start; \ + HalpCbusMemoryHole.Element[CbusMemoryHoleIndex].Length = length; \ + CbusMemoryHoleIndex++; + +#define AddMemoryResource(start, length) \ + HalpCbusMemoryResource.Element[CbusMemoryResourceIndex].Start = start; \ + HalpCbusMemoryResource.Element[CbusMemoryResourceIndex].Length = length; \ + CbusMemoryResourceIndex++; + +#endif // _CBUS_ diff --git a/private/ntos/nthals/halcbus/i386/cbus.inc b/private/ntos/nthals/halcbus/i386/cbus.inc new file mode 100644 index 000000000..11484238e --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus.inc @@ -0,0 +1,277 @@ +;++ +; +; Copyright (c) 1992, 1993, 1994 Corollary Inc +; +; Module Name: +; +; cbus.inc +; +; Abstract: +; +; Corollary MP include file +; +; Author: +; +; Landy Wang (landy@corollary.com) 26-Mar-1992 +;-- + + + +;***************************** +; +; Corollary MP defines +; +; this should be built from a C header file to minimize +; the possiblity of falling out of sync. +; +;***************************** + + +; +; Well known virtual address of the task priority register +; + +LOCALAPIC equ 0fffe0000h +APIC equ ds:[LOCALAPIC] + +; +; The SPURIOUS task priority varies depending on Cbus platform. +; It is set when we initialize the platform and cannot be declared +; here. APC and DPC vectors are the same for Cbus1 and Cbus2 +; so they can be inlined here. +; + +APC_TASKPRI equ 01Fh ; Hardware priority of APCs +DPC_TASKPRI equ 02Fh ; Hardware priority of DPCs + +CBUS2_LEVEL_TRIGGERED_INTERRUPT equ 1 +CBUS2_EDGE_TRIGGERED_INTERRUPT equ 2 + +; +; Here is the assembly version of the CBUS_NTHAL structure declared in cbus_nt.h +; + +RequestIPI equ 000h +HalRequestSWIntr equ 004h +HalQueryPerformanceCounter equ 008h +HalBootCPU equ 00ch + +HalInitializePlatform equ 010h +HalInitializeCPU equ 014h +EnableNonDeviceInterrupt equ 018h +EnableDeviceInterrupt equ 01ch + +DisableInterrupt equ 020h +LinkInterrupt equ 024h +MapVector equ 028h +ParseRRD equ 02ch + +ResolveNMI equ 030h +HalInitInterrupts equ 034h +HalResetAllOtherProcessors equ 038h +HalInitOtherBuses equ 03ch + +HalSetTimeIncrement equ 040h +HalTranslateAddress equ 044h + +; +; The kernel leaves some space in the PCR for the HAL to use +; as it needs. Currently this space is used for some efficiency in +; some of the MP specific code and is highly implementation +; dependent. this order MUST match the definitions in cbus_nt.h. +; + +PcrE struc + PcrNumber dd 0 ; this CPU's logical CPU number + PcrBit dd 0 ; this CPU's logical bit number + PcrCSR dd 0 ; pointer to this CPU's CSR + PcrTaskpri dd 0 ; taskpri reg ptr + PcrBroadcast dd 0 ; interrupt everyone but this CPU + PcrAllOthers dd 0 ; all other CPUs but this one + PcrLEDOn dd 0 ; pointer to this CPU's LED ON + PcrLEDOff dd 0 ; pointer to this CPU's LED OFF + PcrTickOffset dd 0 ; nsec left before calling + ; KeUpdateRunTime on CPU1 or above +PcrE ends + +; +; Offsets of various registers needed for the context switch when +; a system reboot is initiated by an additional processor. +; the space for this array is declared in cbus.c - so bump it there +; if you need more... +; + +REBOOT_EIP equ 0 +REBOOT_ESP equ 4 +REBOOT_EBP equ 8 +REBOOT_EBX equ 12 +REBOOT_ESI equ 16 +REBOOT_EDI equ 20 +REBOOT_EFL equ 24 +REBOOT_CR3 equ 28 + + +; +; ack the interrupt controller (CBC or APIC). +; +; we must derive the bus number so we know which bus' interrupt +; controller to EOI for systems with multiple I/O busses. +; so translate the vector to a bus/irqline pair for the EOI... +; +; WARNING: for the APIC, this will automatically lower the +; internal priority register to the level it was prior to +; receipt of the interrupt. +; +; the caller must initialize scratchreg to the interrupting vector. +; + +CBUS_EOI macro reg1, reg2 +local fix_level +local check_cpu +local do_eoi +local no_eoi + ; + ; reg1 holds the vector that originally interrupted execution, + ; now translate the vector to the correct bridge-based EOI address. + ; + cmp reg1, [_Cbus2ClockVector] + je short check_cpu + cmp [_Cbus2FixLevelInterrupts], 1 + jne short do_eoi + ; + ; first check the polarity of the interrupt (ie. level or edge) + ; to determine whether the level-triggered interrupt hardware + ; work-around need be applied. need to ensure that no interrupts + ; occur while we're performing the workaround for the level-triggered + ; interrupt fix. the fix consists of clearing and resetting the + ; hardware interrupt map entry for this vector. this will cause + ; an internal latch in the CBC to be cleared. this problem will + ; be fixed with the PCIB, at which time, RRD will pass a bit in + ; the configuration table which will cause this code to no longer + ; be executed. + ; +fix_level: + movzx reg2, byte ptr [_Cbus2InterruptPolarity + reg1] + cmp reg2, CBUS2_LEVEL_TRIGGERED_INTERRUPT + jne short do_eoi + mov reg2, dword ptr [_CbusVectorToEoi+4*reg1] + sub reg2, [_Cbus2EoiToHwmap] + mov reg1, dword ptr [_CbusVectorToHwmap+4*reg1] + pushfd + cli + mov dword ptr [reg2], 0 + mov dword ptr [reg2], reg1 ; must _write_ EOI + popfd + jmp short no_eoi +check_cpu: + ; + ; on C-bus II, the clock interrupt must only be EOI'd by + ; a single CPU. make it the boot CPU so that this code + ; will work when only 1 CPU is present. + ; + cmp dword ptr PCR[PcHal.PcrNumber], 0 + jne short no_eoi + cmp [_Cbus2FixLevelInterrupts], 1 + je short fix_level +do_eoi: + mov reg2, dword ptr [_CbusVectorToEoi+4*reg1] + mov dword ptr dword ptr [reg2], reg1 ; must _write_ EOI +no_eoi: +endm + +POKE_LEDS macro reg1, reg2 +local ledset + mov reg1, PCR[PcPrcb] ; point at Prcb + mov reg2, [reg1].PbCurrentThread ; Current thread + cmp reg2, [reg1].PbIdleThread ; Idle Thread + je short @f + + mov reg1, PCR[PcHal.PcrLEDOn] ; get my LED ON address + mov dword ptr [reg1], 1 ; set it + jmp short ledset + +@@: + mov reg1, PCR[PcHal.PcrLEDOff] ; get my LED OFF address + mov dword ptr [reg1], 0 ; set it +ledset: + endm + +; +; Declare all the externs that most of our MASM code will be using +; + + extrn _CbusLocalApic: DWORD + extrn _CbusApicRedirPort: DWORD + extrn _CbusIOApic: DWORD + + extrn _ProfileVector: DWORD + + ; + ; vectors used to communicate reboot and redirection table requested + ; changes to the boot processor. also the global interprocessor ; interrupt vector that any processor can use to interrupt any other + ; processor(s). + ; + extrn _CbusIpiVector: DWORD + extrn _CbusRebootVector: DWORD + extrn _CbusRedirVector: DWORD + + extrn _CbusClockVector: DWORD + extrn _CbusQueryPerformanceCounter: DWORD + extrn _CbusVectorToIrql: DWORD + extrn _CbusIrqlToVector: DWORD + extrn _CbusBackend: DWORD + extrn _CbusRequestIPI: DWORD + extrn _CbusRequestSoftwareInterrupt: DWORD + extrn _CbusProcessorMask: DWORD + extrn _CbusBootedProcessors: DWORD + extrn _CbusRebootRegs: DWORD + + extrn _Cbus2BridgesFound: DWORD + extrn _Cbus2SendIPI: DWORD + extrn _Cbus2Poke8042: DWORD + extrn _Cbus2IrqlToCbus2Addr: DWORD + extrn _Cbus2FixLevelInterrupts: DWORD + extrn _Cbus2CheckSpuriousClock: DWORD + extrn _Cbus2EnableBroadcast: DWORD + extrn _Cbus2ClockVector: DWORD + extrn _Cbus2InterruptPolarity: DWORD + extrn _Cbus2EoiToHwmap: DWORD + extrn _CbusTimeStamp: DWORD + extrn _Cbus2TimeStamp0: DWORD + extrn _CbusVectorToEoi: DWORD + extrn _CbusVectorToHwmap: DWORD + extrn _CbusProcessors: DWORD + + extrn _HalpFindFirstSetRight: BYTE + extrn _Halp8254Lock: DWORD + extrn _MppIDT: DWORD + extrn _MpLowStub: DWORD + extrn _MpLowStubPhysicalAddress: DWORD + extrn _MpCount: DWORD + +; +; Declare all the functions that our MASM code will be calling +; + + EXTRNP _DbgBreakPoint,0,IMPORT + + EXTRNP _HalEnableSystemInterrupt, 3 + EXTRNP _HalDisableSystemInterrupt, 2 + EXTRNP _HalpAcquireCmosSpinLock, 0 + EXTRNP _HalpReleaseCmosSpinLock, 0 + + EXTRNP _KeBugCheck,1,IMPORT + EXTRNP _KeSetTimeIncrement,2,IMPORT + EXTRNP _KeUpdateRunTime,1,IMPORT + EXTRNP _KeUpdateSystemTime,0 + EXTRNP Kei386EoiHelper,0,IMPORT + + EXTRNP _KiIpiServiceRoutine,2,IMPORT + EXTRNP _KiDeliverApc,3,IMPORT + EXTRNP _KiDispatchInterrupt,0,IMPORT + + EXTRNP _ExAllocatePool,2,IMPORT + + EXTRNP _HalpBuildTiledCR3,1 + EXTRNP _HalpFreeTiledCR3,0 + EXTRNP _HalpProfileInterrupt2ndEntry,0 diff --git a/private/ntos/nthals/halcbus/i386/cbus1.c b/private/ntos/nthals/halcbus/i386/cbus1.c new file mode 100644 index 000000000..0c64667bc --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus1.c @@ -0,0 +1,1870 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus1.c + +Abstract: + + This module implements the initialization of the system dependent + functions that define the Hardware Architecture Layer (HAL) for the + Corollary Cbus1 MP machines under Windows NT. + +Author: + + Landy Wang (landy@corollary.com) 05-Oct-1992 + +Environment: + + Kernel mode only. + +Revision History: + + +--*/ + +#include "halp.h" +#include "cbusrrd.h" // HAL <-> RRD interface definitions +#include "cbus.h" // Cbus1 & Cbus2 max number of elements is here +#include "cbus1.h" // Cbus1 hardware architecture stuff +#include "cbus_nt.h" // C-bus NT-specific implementation stuff +#include "bugcodes.h" +#include "stdio.h" +#include "cbusnls.h" +#include "cbusapic.h" // Cbus APIC generic definitions + + +PULONG +CbusApicVectorToEoi( +IN ULONG Vector +); + +VOID +Cbus1PerfInterrupt(VOID); + +PVOID +Cbus1LinkVector( +IN PBUS_HANDLER Bus, +IN ULONG Vector, +IN ULONG Irqline +); + +VOID +Cbus1InitializeStall(IN ULONG); + +VOID +Cbus1InitializeClock(VOID); + +VOID +Cbus1InitializePerf(VOID); + +ULONG +Cbus1QueryInterruptPolarity(VOID); + +VOID +Cbus1BootCPU( +IN ULONG Processor, +IN ULONG RealModeSegOff +); + +VOID +Cbus1InitializePlatform(VOID); + +VOID +CbusPreparePhase0Interrupts( +IN ULONG, +IN ULONG, +IN PVOID +); + +VOID +Cbus1InitializeCPU( +IN ULONG Processor +); + +VOID +Cbus1ECCEnable(VOID); + +VOID +Cbus1ECCDisable(VOID); + +PUCHAR +CbusIDtoCSR( +IN ULONG ArbID +); + +VOID +Cbus1Arbitrate(VOID); + +VOID +Cbus1HandleJumpers(); + +VOID +Cbus1ParseRRD( +IN PEXT_ID_INFO Table, +IN OUT PULONG Count +); + +PVOID +Cbus1FindDeadID( +IN ULONG ArbID +); + +VOID +Cbus1InitializePlatform(); + +VOID +Cbus1InitializeDeviceIntrs( +IN ULONG Processor +); + +VOID +Cbus1SetupPrivateVectors(VOID); + +VOID +CbusRebootHandler( VOID ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, Cbus1SetupPrivateVectors) +#pragma alloc_text(INIT, Cbus1BootCPU) +#pragma alloc_text(INIT, Cbus1InitializePlatform) +#pragma alloc_text(INIT, Cbus1InitializeCPU) +#pragma alloc_text(INIT, Cbus1ECCEnable) +#pragma alloc_text(INIT, Cbus1ECCDisable) +#pragma alloc_text(INIT, CbusIDtoCSR) +#pragma alloc_text(INIT, Cbus1Arbitrate) +#pragma alloc_text(INIT, Cbus1HandleJumpers) +#pragma alloc_text(INIT, Cbus1ParseRRD) +#pragma alloc_text(INIT, Cbus1FindDeadID) +#pragma alloc_text(INIT, Cbus1InitializeDeviceIntrs) +#pragma alloc_text(INIT, Cbus1QueryInterruptPolarity) +#endif + +extern PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1]; + +PUCHAR Cbus1ID0CSR; + +// +// Used for holding IDs of disabled Cbus1 processors. This is +// done to detect whether the user has inserted any obsolete +// boards, since they can still win arbitrations. Thus, we can +// detect disabled arbitration winners from just plain broken systems. +// +PEXT_ID_INFO Cbus1DeadIDs[MAX_ELEMENT_CSRS]; +ULONG Cbus1DeadIDsIndex; + +// +// The limited priority scheme employed by the Intel APIC is as follows: +// +// there are 256 vectors, but since the APIC ignores the least +// significant 4 bits, we really only have 16 distinct priority levels +// to work with. +// +// processor traps, NT system calls and APC/DPC use up the first 0x30 vectors. +// after the required Power, IPI, Clock and Profiling levels, +// there really isn't much left. see the picture below: +// +// APC: 0x1F (lowest priority) +// DPC: 0x2F +// +// Lowest Priority EISA: 0x30 real devices here +// Highest Priority EISA: 0xBF real devices here +// +// EISA Profile: 0xCF +// EISA Clock(Perf Counter): 0xDD +// APIC Clock (System Timer): 0xDF +// Spurious Vector: 0xEC +// CBUS1_REBOOT_IPI: 0xED +// CBUS1_REDIR_IPI: 0xEE +// IPI: 0xEF +// Power: 0xFF +// High: 0xFF (highest priority) +// +// we have only 9 distinct priority levels left!!! +// +// due to this APIC shortcoming, we are forced to share levels between +// various EISA irq lines. each line will be assigned a different vector, +// and the IRQL mappings will work, but they will not be optimal. +// +// note that the Cbus2 CBC suffers from none of these shortcomings. +// + +// +// set unused irql levels to the priority of the irql level above it +// so that if they are inadvertently called from the kernel, +// executive or drivers, nothing bad will happen. +// + +#define UNUSED_PRI CBUS1_PROFILE_TASKPRI + +#define CBUS1_DEVICELOW_TASKPRI 0x30 +#define CBUS1_DEVICEHI_TASKPRI 0xBF + +#define CBUS1_IRQ2_TASKPRI 0xCE // not expected to happen +#define CBUS1_PROFILE_TASKPRI 0xCF +#define CBUS1_PERF_TASKPRI 0xDD // also in cb1stall.asm +#define CBUS1_CLOCK_TASKPRI 0xDF + +#define CBUS1_REBOOT_IPI 0xED +#define CBUS1_REDIR_IPI 0xEE +#define CBUS1_IPI_TASKPRI 0xEF + +#define CBUS1_POWER_TASKPRI 0xFE + +// +// we don't really care what the spurious value is, but the APIC +// spec seems to imply that this must be hardcoded at 0xff for future +// compatibility. +// +#define CBUS1_SPURIOUS_TASKPRI 0xFF + +// +// since we have 9 priority levels and 13 irq lines, priorities are ordered +// in buckets as follows, from high priority to low priority: +// +// highest priority to keyboard: (usually irq1) +// +// next highest priority to serial port/mouse/network card. (usually irq3) +// +// next highest priority to serial port/network card. (usually irq4) +// +// next highest priority to SCSI/hard disks/networks. (usually irq9) +// +// next highest priority to SCSI/hard disks/networks. (usually irqA) +// +// next highest priority to SCSI/hard disks/networks. (usually irqB or C) +// +// next highest priority to SCSI/hard disks/networks. (usually irqD or E) +// +// next highest priority to SCSI/disks/networks/printers. (usually irqF or 5) +// +// lowest device priority to floppy, printers. (usually irq6 or 7) +// +#define EISA_IRQ0PRI CBUS1_PERF_TASKPRI // 8254 line (perf ctr) & pri +#define EISA_IRQ1PRI (CBUS1_DEVICELOW_TASKPRI + 0x80) +#define EISA_IRQ2PRI CBUS1_IRQ2_TASKPRI +#define EISA_IRQ3PRI (CBUS1_DEVICELOW_TASKPRI + 0x70) + +#define EISA_IRQ4PRI (CBUS1_DEVICELOW_TASKPRI + 0x60) +#define EISA_IRQ5PRI (CBUS1_DEVICELOW_TASKPRI + 0x10) +#define EISA_IRQ6PRI (CBUS1_DEVICELOW_TASKPRI + 0x01) +#define EISA_IRQ7PRI (CBUS1_DEVICELOW_TASKPRI + 0x00) + +#define EISA_IRQ8PRI CBUS1_PROFILE_TASKPRI // profile line & priority +#define EISA_IRQ9PRI (CBUS1_DEVICELOW_TASKPRI + 0x50) +#define EISA_IRQAPRI (CBUS1_DEVICELOW_TASKPRI + 0x40) +#define EISA_IRQBPRI (CBUS1_DEVICELOW_TASKPRI + 0x31) + +#define EISA_IRQCPRI (CBUS1_DEVICELOW_TASKPRI + 0x30) +#define EISA_IRQDPRI (CBUS1_DEVICELOW_TASKPRI + 0x21) +#define EISA_IRQEPRI (CBUS1_DEVICELOW_TASKPRI + 0x20) +#define EISA_IRQFPRI (CBUS1_DEVICELOW_TASKPRI + 0x11) + +// +// an EISA_IRQ2PRI is declared above just to flesh out arrays that will +// be indexed by irq line. the EISA_IRQ2PRI should never actually be used +// + +#if (HIGH_LEVEL + 1 != 32) +Cbus1IrqlToVector[] NOT dimensioned and indexed properly +#endif + +ULONG Cbus1IrqlToVector[HIGH_LEVEL + 1 ] = { + + LOW_TASKPRI, APC_TASKPRI, DPC_TASKPRI, EISA_IRQ7PRI, + EISA_IRQ6PRI, EISA_IRQ5PRI, EISA_IRQFPRI, EISA_IRQEPRI, + EISA_IRQDPRI, EISA_IRQCPRI, EISA_IRQBPRI, EISA_IRQAPRI, + EISA_IRQ9PRI, EISA_IRQ4PRI, EISA_IRQ3PRI, EISA_IRQ1PRI, + + EISA_IRQ2PRI, UNUSED_PRI, UNUSED_PRI, UNUSED_PRI, + UNUSED_PRI, UNUSED_PRI, UNUSED_PRI, UNUSED_PRI, + UNUSED_PRI, UNUSED_PRI, UNUSED_PRI, CBUS1_PROFILE_TASKPRI, + CBUS1_CLOCK_TASKPRI, CBUS1_IPI_TASKPRI, CBUS1_POWER_TASKPRI, HIGH_TASKPRI, + +}; + +ULONG Cbus1IrqPolarity; + +#define IRQPERFLINE 0 +#define EISA_IRQLINES 16 + +typedef struct _cbus1_irqline_t { + ULONG Vector; + KIRQL Irql; +} CBUS1_IRQLINE_T, *PCBUS1_IRQLINE; + +// +// map EISA irqline to Cbus1 programmable vectors & IRQL levels, +// as defined above. +// + +#define FIRST_DEVICE_LEVEL (DISPATCH_LEVEL+1) + +CBUS1_IRQLINE_T Cbus1Irqlines[EISA_IRQLINES] = +{ + EISA_IRQ0PRI, CLOCK2_LEVEL, + EISA_IRQ1PRI, FIRST_DEVICE_LEVEL+12, + EISA_IRQ2PRI, FIRST_DEVICE_LEVEL+13, + EISA_IRQ3PRI, FIRST_DEVICE_LEVEL+11, + + EISA_IRQ4PRI, FIRST_DEVICE_LEVEL+10, + EISA_IRQ5PRI, FIRST_DEVICE_LEVEL+2, + EISA_IRQ6PRI, FIRST_DEVICE_LEVEL+1, + EISA_IRQ7PRI, FIRST_DEVICE_LEVEL, + + EISA_IRQ8PRI, PROFILE_LEVEL, + EISA_IRQ9PRI, FIRST_DEVICE_LEVEL+9, + EISA_IRQAPRI, FIRST_DEVICE_LEVEL+8, + EISA_IRQBPRI, FIRST_DEVICE_LEVEL+7, + + EISA_IRQCPRI, FIRST_DEVICE_LEVEL+6, + EISA_IRQDPRI, FIRST_DEVICE_LEVEL+5, + EISA_IRQEPRI, FIRST_DEVICE_LEVEL+4, + EISA_IRQFPRI, FIRST_DEVICE_LEVEL+3 +}; + +PUCHAR Cbus1ResetVector; + +extern ULONG CbusRedirVector; +extern ULONG CbusRebootVector; + +extern ADDRESS_USAGE HalpCbusMemoryResource; +extern ULONG CbusMemoryResourceIndex; + +// +// defines for the Cbus1 ECC syndrome +// +#define MULTIBIT 3 +#define DOUBLEBIT 2 +#define SINGLEBIT 1 +#define NOECCERROR 0x7f + +// +// defines for the Cbus1 ECC error register +// +typedef struct _extmear_t { + ULONG offset:24; + ULONG SyndromeHigh:5; + ULONG SyndromeLow:2; + ULONG simmtype:1; +} EXTMEAR_T, *PEXTMEAR; + +UCHAR Cbus1EdacSyndrome[] = { + +MULTIBIT, /* 00 */ DOUBLEBIT, /* 01 */ DOUBLEBIT, /* 02 */ MULTIBIT, /* 03 */ +DOUBLEBIT, /* 04 */ MULTIBIT, /* 05 */ MULTIBIT, /* 06 */ DOUBLEBIT, /* 07 */ +DOUBLEBIT, /* 08 */ MULTIBIT, /* 09 */ SINGLEBIT, /* 0A */ DOUBLEBIT, /* 0B */ +MULTIBIT, /* 0C */ DOUBLEBIT, /* 0D */ DOUBLEBIT, /* 0E */ SINGLEBIT, /* 0F */ +DOUBLEBIT, /* 10 */ MULTIBIT, /* 11 */ SINGLEBIT, /* 12 */ DOUBLEBIT, /* 13 */ +SINGLEBIT, /* 14 */ DOUBLEBIT, /* 15 */ DOUBLEBIT, /* 16 */ SINGLEBIT, /* 17 */ +SINGLEBIT, /* 18 */ DOUBLEBIT, /* 19 */ DOUBLEBIT, /* 1A */ SINGLEBIT, /* 1B */ +DOUBLEBIT, /* 1C */ SINGLEBIT, /* 1D */ MULTIBIT, /* 1E */ DOUBLEBIT, /* 1F */ +DOUBLEBIT, /* 20 */ MULTIBIT, /* 21 */ SINGLEBIT, /* 22 */ DOUBLEBIT, /* 23 */ +MULTIBIT, /* 24 */ DOUBLEBIT, /* 25 */ DOUBLEBIT, /* 26 */ SINGLEBIT, /* 27 */ +SINGLEBIT, /* 28 */ DOUBLEBIT, /* 29 */ DOUBLEBIT, /* 2A */ SINGLEBIT, /* 2B */ +DOUBLEBIT, /* 2C */ SINGLEBIT, /* 2D */ MULTIBIT, /* 2E */ DOUBLEBIT, /* 2F */ +SINGLEBIT, /* 30 */ DOUBLEBIT, /* 31 */ DOUBLEBIT, /* 32 */ MULTIBIT, /* 33 */ +DOUBLEBIT, /* 34 */ SINGLEBIT, /* 35 */ MULTIBIT, /* 36 */ DOUBLEBIT, /* 37 */ +DOUBLEBIT, /* 38 */ MULTIBIT, /* 39 */ MULTIBIT, /* 3A */ DOUBLEBIT, /* 3B */ +MULTIBIT, /* 3C */ DOUBLEBIT, /* 3D */ DOUBLEBIT, /* 3E */ SINGLEBIT, /* 3F */ +DOUBLEBIT, /* 40 */ MULTIBIT, /* 41 */ MULTIBIT, /* 42 */ DOUBLEBIT, /* 43 */ +MULTIBIT, /* 44 */ DOUBLEBIT, /* 45 */ DOUBLEBIT, /* 46 */ MULTIBIT, /* 47 */ +MULTIBIT, /* 48 */ DOUBLEBIT, /* 49 */ DOUBLEBIT, /* 4A */ SINGLEBIT, /* 4B */ +DOUBLEBIT, /* 4C */ MULTIBIT, /* 4D */ SINGLEBIT, /* 4E */ DOUBLEBIT, /* 4F */ +MULTIBIT, /* 50 */ DOUBLEBIT, /* 51 */ DOUBLEBIT, /* 52 */ SINGLEBIT, /* 53 */ +DOUBLEBIT, /* 54 */ SINGLEBIT, /* 55 */ SINGLEBIT, /* 56 */ DOUBLEBIT, /* 57 */ +DOUBLEBIT, /* 58 */ SINGLEBIT, /* 59 */ SINGLEBIT, /* 5A */ DOUBLEBIT, /* 5B */ +SINGLEBIT, /* 5C */ DOUBLEBIT, /* 5D */ DOUBLEBIT, /* 5E */ SINGLEBIT, /* 5F */ +MULTIBIT, /* 60 */ DOUBLEBIT, /* 61 */ DOUBLEBIT, /* 62 */ SINGLEBIT, /* 63 */ +DOUBLEBIT, /* 64 */ SINGLEBIT, /* 65 */ SINGLEBIT, /* 66 */ DOUBLEBIT, /* 67 */ +DOUBLEBIT, /* 68 */ SINGLEBIT, /* 69 */ SINGLEBIT, /* 6A */ DOUBLEBIT, /* 6B */ +SINGLEBIT, /* 6C */ DOUBLEBIT, /* 6D */ DOUBLEBIT, /* 6E */ SINGLEBIT, /* 6F */ +DOUBLEBIT, /* 70 */ SINGLEBIT, /* 71 */ MULTIBIT, /* 72 */ DOUBLEBIT, /* 73 */ +SINGLEBIT, /* 74 */ MULTIBIT, /* 75 */ DOUBLEBIT, /* 76 */ SINGLEBIT, /* 77 */ +MULTIBIT, /* 78 */ DOUBLEBIT, /* 79 */ DOUBLEBIT, /* 7A */ SINGLEBIT, /* 7B */ +DOUBLEBIT, /* 7C */ SINGLEBIT, /* 7D */ SINGLEBIT, /* 7E */ NOECCERROR,/* 7F */ + +}; + +VOID +FatalError( +IN PUCHAR ErrorString +); + +VOID +CbusHardwareFailure( +IN PUCHAR HardwareMessage +); + +VOID +CbusClockInterruptPx( VOID ); + +VOID +HalpProfileInterruptPx( VOID ); + +VOID +Cbus1Boot1( +IN ULONG Processor, +IN PQUAD Dest, +IN PQUAD Code, +IN ULONG ResetAddress, +IN ULONG ResetValue +); + +extern ULONG Cbus1Boot1End; + +VOID +Cbus1Boot2( +IN ULONG Processor, +IN PQUAD Dest, +IN PQUAD Code, +IN ULONG ResetAddress, +IN ULONG ResetValue +); + +// +// defines for resetting the keyboard controller used +// in Cbus1ResetAllOtherProcessors(). +// +#define RESET 0xfe +#define KEYBPORT (PUCHAR )0x64 + +#define IRQ0 0 +#define IRQ8 8 + +/*++ + +Routine Description: + + This routine is called only once from HalInitProcessor() at Phase 0 + by the boot cpu. All other cpus are still in reset. + + software(APC, DPC, wake) and IPI vectors have already been initialized + and enabled. + + all we're doing here is setting up some software structures for two + EISA interrupts (8254 perfctr and RTC profile) so they can be enabled later. + + The bus handler data structures are not initialized until Phase 1, + so HalGetInterruptVector() may not be called before Phase1. + + Hence we cannot pass a valid BusHandler parameter to the Link code. + That's ok, since it doesn't currently use it. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +VOID +Cbus1SetupPrivateVectors(VOID) +{ + PVOID Opaque; + extern ULONG ProfileVector; + + // + // we are defaulting to the EISA (or MCA) bridge 0 + // for all of these interrupts which need enabling during Phase 0. + // + + Opaque = Cbus1LinkVector((PBUS_HANDLER)0, CBUS1_PERF_TASKPRI, IRQ0); + CbusPreparePhase0Interrupts(CBUS1_PERF_TASKPRI, IRQ0, Opaque); + + Opaque = Cbus1LinkVector((PBUS_HANDLER)0, ProfileVector, IRQ8); + CbusPreparePhase0Interrupts(ProfileVector, IRQ8, Opaque); +} + +VOID +Cbus1BootCPU( +IN ULONG Processor, +IN ULONG RealModeSegOff +) +/*++ + +Routine Description: + + Remove reset from the specified processor, allowing him to boot, + beginning execution at the specified start address. This ends up + being tricky due to cache flushing concepts generally hidden in ROMs, + but in the HAL for the Cbus1 platform. + +Arguments: + + Processor - Supplies a logical processor number to boot + + StartAddress - Supplies a start address containing real-mode code + for the processor to execute. + +Return Value: + + None. + +--*/ +{ + PHYSICAL_ADDRESS Bootcode; + ULONG BeginAddress, EndAddress; + ULONG BeginCacheLineIndex; + ULONG EndCacheLineIndex; + UCHAR StartVectorCode[5]; + PVOID PhysicalResetVector; + ULONG ip = RealModeSegOff & 0xFFFF; + ULONG cs = ((RealModeSegOff >> 16) & 0xFFFF); + ULONG CacheFlush = CBUS1_CACHE_SIZE; + ULONG LastCacheLineIndex; + + // + // if we haven't yet mapped in the reset vector, do so now. + // + + if (!Cbus1ResetVector) { + + PhysicalResetVector = + (PUCHAR)CbusGlobal.resetvec - CBUS1_CACHE_LINE; + + Cbus1ResetVector = (PUCHAR)HalpMapPhysicalMemory ( + PhysicalResetVector, 1); + } + + + // + // build start vector (entry offset followed by cs load base) + // + StartVectorCode[0] = (UCHAR)0xea; + StartVectorCode[1] = (UCHAR)ip & 0xff; + StartVectorCode[2] = (UCHAR)(ip >> 8) & 0xff; + StartVectorCode[3] = (UCHAR)cs & 0xff; + StartVectorCode[4] = (UCHAR)(cs >> 8) & 0xff; + + // + // determine which low-level boot function to use. although + // the 2 functions are exactly the same, we must make sure that + // we don't call one which would cause the cache line containing + // the reset vector to be evicted prematurely. + // + + LastCacheLineIndex = ((CacheFlush - 1) >> CBUS1_CACHE_SHIFT); + + Bootcode = MmGetPhysicalAddress(&Cbus1Boot1); + BeginAddress = Bootcode.LowPart; + BeginCacheLineIndex = + (BeginAddress & (CacheFlush - 1)) >> CBUS1_CACHE_SHIFT; + + Bootcode = MmGetPhysicalAddress((PVOID)&Cbus1Boot1End); + EndAddress = Bootcode.LowPart; + EndCacheLineIndex = + (EndAddress & (CacheFlush - 1)) >> CBUS1_CACHE_SHIFT; + + // + // in order to startup additional CPUs, we need to write its + // startup vector in the last 16 bytes of the memory + // address space. this memory may or may not be + // present. more specifically, a partially populated + // memory card which doesn't have memory at the end of + // the space, ie: a memory card at 48Mb->56Mb in a + // system with a 64Mb upper limit) will cause an ecc + // error if ecc is enabled. + // + // thus we must disable ecc to prevent such boards from + // generating ecc errors. note that the errors are only + // generated on the read when we initially fill the + // startvector. it's ok to enable ecc after startup + // even though the cacheline containing the StartVectorCode + // has not necessarily been flushed. when it is flushed, + // it will go in the bit bucket and ecc errors are + // generated only on reads (not writes). + // + + Cbus1ECCDisable(); + + // + // call Cbus1Boot1 if there is no wrap... + // see the detailed comment at the top of Cbus1Boot1 for more details. + // + if ((EndCacheLineIndex != LastCacheLineIndex) && EndAddress > BeginAddress) { + Cbus1Boot1(Processor, + (PQUAD) Cbus1ResetVector, + (PQUAD) StartVectorCode, + (ULONG)CbusCSR[Processor].csr + + CbusGlobal.smp_creset, + CbusGlobal.smp_creset_val); + } + else { + Cbus1Boot2(Processor, + (PQUAD) Cbus1ResetVector, + (PQUAD) StartVectorCode, + (ULONG)CbusCSR[Processor].csr + + CbusGlobal.smp_creset, + CbusGlobal.smp_creset_val); + } + + Cbus1ECCEnable(); +} + +VOID +Cbus1InitializePlatform(VOID) +/*++ + +Routine Description: + + Overlay the irql-to-vector mappings with the Cbus1 vector maps. + Give all additional processors proper bus arbitration IDs. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + RtlMoveMemory( (PVOID)CbusIrqlToVector, + (PVOID)Cbus1IrqlToVector, + (HIGH_LEVEL + 1) * sizeof (ULONG)); + + Cbus1IrqPolarity = Cbus1QueryInterruptPolarity(); + + // + // if there are any Cbus1 additional processors present, + // put them into reset and arbitrate them so they get + // branded with proper IDs. + // + if (CbusProcessors > 1) + Cbus1Arbitrate(); + + CbusRedirVector = CBUS1_REDIR_IPI; + CbusRebootVector = CBUS1_REBOOT_IPI; +} + + +VOID +Cbus1InitializeCPU( +IN ULONG Processor +) +/*++ + +Routine Description: + + Initialize this processor's local and I/O APIC units. + +Arguments: + + Processor - Supplies a logical processor number + +Return Value: + + None. + +--*/ +{ + CbusInitializeLocalApic(Processor, CBUS1_LOCAL_APIC_LOCATION, + CBUS1_SPURIOUS_TASKPRI); + + // + // Only the boot processor needs to initialize the I/O APIC + // on the EISA bridge. Each additional processor just needs + // to give his I/O APIC (since it won't be used) an arbitration ID. + // + + if (Processor == 0) { + CbusInitializeIOApic(Processor, CBUS1_IO_APIC_LOCATION, + CBUS1_REDIR_IPI, CBUS1_REBOOT_IPI, Cbus1IrqPolarity); + } + else { + KiSetHandlerAddressToIDT(CBUS1_REBOOT_IPI, CbusRebootHandler); + HalEnableSystemInterrupt(CBUS1_REBOOT_IPI, IPI_LEVEL, Latched); + CbusApicBrandIOUnitID(Processor); + } +} + + +BOOLEAN +Cbus1EnableNonDeviceInterrupt( +IN ULONG Vector +) +/*++ + +Routine Description: + + Determine if the supplied vector belongs to an EISA device - if so, + then the corresponding I/O APIC entry will need to be modified to + enable the line, so return FALSE here. Otherwise, no work is needed, + so just return TRUE immediately. + +Arguments: + + Vector - Supplies a vector number to enable + +Return Value: + + TRUE if the vector was enabled, FALSE if not. + +--*/ +{ + // + // If pure software interrupt, no action needed. + // Note both APIC timer interrupts and IPIs are + // treated as "software" interrupts. Note that + // an EOI will still be needed, so set that up here. + // + + if (Vector != CBUS1_SPURIOUS_TASKPRI) + CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector); + + if (Vector < CBUS1_DEVICELOW_TASKPRI || Vector >= CBUS1_CLOCK_TASKPRI) { + return TRUE; + } + + // + // indicate that Enable_Device_Interrupt will need to be run + // in order to enable this vector, as it associated with an I/O + // bus device which will need enabling within a locked critical + // section. + // + + return FALSE; +} + + +VOID +Cbus1EnableDeviceInterrupt( +IN ULONG Vector, +IN PVOID HardwarePtr, +IN ULONG FirstAttach, +IN USHORT BusNumber, +IN USHORT Irqline +) +/*++ + +Routine Description: + + Enable the specified interrupt for the calling processor. + Remember only the boot processor can add/remove processors from + the I/O APIC's redirection entries. + + This operation is trivial for the boot processor. However, additional + processors must interrupt the boot processor with an "enable interrupt" + request and then spin waiting for the boot processor to acknowledge that + the entry has been modified. Note that the caller holds the HAL's + CbusVectorLock at CLOCK_LEVEL on entry. + +Arguments: + + Vector - Supplies a vector number to enable + + HardwarePtr - Supplies a redirection entry address + + FirstAttach - TRUE if this is the first processor to enable the + specified vector + +Return Value: + + None. + +--*/ +{ + BOOLEAN LowestInGroup; + BOOLEAN LevelTriggered; + + // + // Only 1 I/O bus in Cbus1 systems, only Cbus2 can have more + // + ASSERT(BusNumber == 0); + + // + // Set up the EOI address + // + CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector); + + // + // Enable APIC LIG arbitration delay (at least 4 cycles on + // our 10Mhz APIC bus, which is 0.4 microseconds). + // + + switch (Vector) { + + case CBUS1_PERF_TASKPRI: + case CBUS1_CLOCK_TASKPRI: + case CBUS1_PROFILE_TASKPRI: + case CBUS1_POWER_TASKPRI: + + // These stay in APIC "FIXED" mode + + LowestInGroup = FALSE; + break; + + default: + + // All others utilize APIC "Lowest-In-Group" + + LowestInGroup = TRUE; + break; + } + + // + // Note that non-EISA interrupts (ie: APIC clocks or IPI) are + // correctly handled in the EnableNonDeviceInterrupt case, and + // hence calls to enable those will never enter this routine. + // If this changes, the code below needs to be modified because + // these interrupts do NOT have an ELCR entry (there are only + // 16 of those). + // + + if (((Cbus1IrqPolarity >> Irqline)) & 0x1) { + LevelTriggered = TRUE; + } + else { + LevelTriggered = FALSE; + } + + CbusEnableApicInterrupt(BusNumber, Vector, HardwarePtr, FirstAttach, + LowestInGroup, LevelTriggered); +} + +VOID +Cbus1DisableInterrupt( +IN ULONG Vector, +IN PVOID HardwarePtr, +IN ULONG LastDetach, +IN USHORT BusNumber, +IN USHORT Irqline +) +/*++ + +Routine Description: + + Disable the specified interrupt so it can not occur on the calling + processor upon return from this routine. Remember only the boot processor + can add/remove processors from his I/O APIC's redirection entries. + + This operation is trivial for the boot processor. However, additional + processors must interrupt the boot processor with a "disable interrupt" + request and then spin waiting for the boot processor to acknowledge that + the entry has been modified. Note that the caller holds the HAL's + CbusVectorLock at CLOCK_LEVEL on entry. + +Arguments: + + Vector - Supplies a vector number to disable + + HardwarePtr - Supplies a redirection entry address + + LastDetach - TRUE if this is the last processor to detach from the + specified vector + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( Irqline ); + + // + // Only 1 I/O bus in Cbus1 systems, only Cbus2 can have more + // + ASSERT(BusNumber == 0); + + CbusDisableApicInterrupt(BusNumber, Vector, HardwarePtr, LastDetach); +} + + +ULONG +Cbus1MapVector( + PBUS_HANDLER Bus, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql + ) +/*++ + +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. + + HalGetInterruptVector() must maintain a "vector to interrupt" + mapping so when the interrupt is enabled later via + HalEnableSystemInterrupt(), something intelligent can be done - + ie: which CBC's hardware interrupt maps to enable! + this applies both to EISA bridge CBCs and C-bus II half-card CBC's. + + Note that HalEnableSystemInterrupt() will be CALLED by + EACH processor wishing to participate in the interrupt receipt. + + Do not detect collisions here because interrupts are allowed to be + shared at a higher level - the ke\i386\intobj.c will take care of + the sharing. Just make sure that for any given irq line, only one + vector is generated, regardless of how many drivers may try to share + the line. + +Arguments: + + + BusHandler - per bus specific structure + + BusInterruptLevel - Supplies the bus specific interrupt level. + + BusInterruptVector - Supplies the bus specific interrupt vector. + + Irql - Returns the system request priority. + +Return Value: + + Returns the system interrupt vector corresponding to the specified device. + +--*/ +{ + ULONG SystemVector; + + UNREFERENCED_PARAMETER( BusInterruptVector ); + + SystemVector = Cbus1Irqlines[BusInterruptLevel].Vector; + *Irql = Cbus1Irqlines[BusInterruptLevel].Irql; + return SystemVector; +} + +PVOID +Cbus1LinkVector( +IN PBUS_HANDLER Bus, +IN ULONG Vector, +IN ULONG Irqline +) +/*++ + +Routine Description: + + "Link" a given vector to the passed BusNumber/irqline, returning + a "handle" that can be used to reference it later for operations + that need to access the hardware (ie: Enable & DisableInterrupt). + +Arguments: + + Vector - Supplies the system interrupt vector corresponding to the + specified BusNumber/Irqline. + + Irqline - Supplies the IRQ line of the specified interrupt + +Return Value: + + A hardware-specific pointer (actually a redirection entry address) + that is interpreted only by the Cbus1 backend. + +--*/ +{ + return CbusApicLinkVector(Bus, Vector, Irqline); +} + +#if DBG +#define NMI_BUTTON_PRESSED() 1 +#else +#define NMI_BUTTON_PRESSED() 0 +#endif + +NTSTATUS +Cbus1ResolveNMI( + IN PVOID NmiInfo + ) +/*++ + +Routine Description: + + This function determines the cause of the NMI so that the user can + replace any offending SIMMs. + +Arguments: + + NmiInfo - pointer to the NMI information structure + +Return Value: + + Returns the byte address which caused the NMI, 0 if indeterminate + +--*/ +{ + ULONG Board; + PMEMORY_CARD pm; + ULONG Syndrome; + PEXTMEAR Mear; + PHYSICAL_ADDRESS FaultAddress; + UCHAR NmiMessage[80]; + BOOLEAN founderror = FALSE; + extern VOID CbusClearEISANMI(VOID); + extern NTSTATUS DefaultHalHandleNMI( IN OUT PVOID); + + if (NMI_BUTTON_PRESSED()) { + + // + // NMI button was pressed, so go to the debugger + // + + _asm { + int 3 + } + + // + // Clear the NMI in hardware so the system can continue + // + + CbusClearEISANMI(); + + return STATUS_SUCCESS; // ignore this NMI + } + + if (CbusGlobal.nonstdecc) + return DefaultHalHandleNMI(NmiInfo); + + FaultAddress.LowPart = 0; + FaultAddress.HighPart = 0; + + pm = CbusMemoryBoards; + + for (Board = 0; Board < CbusMemoryBoardIndex; Board++, pm++) { + + if (pm->io_attr == 0) + continue; + + // + // the syndrome/page routines below are for + // the Corollary Cbus1 smp/XM architecture. + // this architecture currently supports 256Mb of RAM. + // + // this method is the defacto standard for Corollary + // Cbus1 licensees with > 64Mb. + // + + Mear = (PEXTMEAR)pm->regmap; + + // + // check whether this memory board generated the ecc error + // double check all the byte vs. clicks conversions and + // also the baseram (for jumpered megabytes) + // + + Syndrome = + Cbus1EdacSyndrome[(Mear->SyndromeHigh<<2)|Mear->SyndromeLow]; + + if (Syndrome == NOECCERROR || Syndrome == SINGLEBIT) + continue; + + // + // Cbus1 will always have less than 4Gig of physical memory, + // so just calculate LowPart here. no need to add in + // CbusGlobal.baseram, because that will always be zero. + // + // the Mear->offset will always be zero based, regardless of + // whether the memory was reclaimed above 256MB or not. + // + + FaultAddress.LowPart = (Mear->offset << 4); + + founderror = TRUE; + + // + // Disable ecc so the system can be safely taken down + // without getting another double-bit error (because the + // kernel may iret later). + // + + Cbus1ECCDisable(); + + break; + } + + if (founderror == TRUE) { + sprintf(NmiMessage, MSG_CBUS1NMI_ECC, 0, FaultAddress.LowPart); + CbusHardwareFailure (NmiMessage); + } + + // + // No errors found in Cbus RAM, so check for EISA errors + // + + DefaultHalHandleNMI(NmiInfo); +} + +VOID +Cbus1ResetAllOtherProcessors( + IN ULONG Processor + ) +/*++ + +Routine Description: + + Called to put all the other processors in reset prior to reboot. + In order to safely put the other processors in reset, an IPI is + sent to the other processors to force them to write-invalidate + their L1 cache and halt. The calling processor will wait 5 seconds + for the IPI to take affect. + + Shadowing is then disabled. The keyboard controller is then + reset and ThisProcessor spins awaiting the reboot. + + This routine can be called at any IRQL. The boot processor can + call whilst cli'd at interrupt time. + +Arguments: + + Processor - Supplies the caller's logical processor number + +Return Value: + + None. + +--*/ +{ + PUCHAR BiosProcessorCSR; + extern VOID Cbus1RebootRequest(IN ULONG); + + _asm { + cli + } + + // + // IPI the other processors to get them to flush + // their internal caches and halt. this will be more + // conducive for the subsequent reset. + // + Cbus1RebootRequest(Processor); + + // + // Delay 5 seconds to give the additional processors + // a chance to get the previous IPI. + // + KeStallExecutionProcessor(5000000); + + // + // issue a global reset to the broadcast address + // + *(PULONG)((PUCHAR)CbusBroadcastCSR + CbusGlobal.smp_sreset) = + CbusGlobal.smp_sreset_val; + + // + // Disable BIOS shadowing until RRD recopies the BIOS ROM + // into shadow RAM again. This is because we "reclaim" the + // BIOS hole up at 256MB + 640K, to use it as general purpose + // (shadowed) RAM for NT. Referring to the ROM at 640K (NOT + // 256MB + 640K) you will see a pristine BIOS copy, but + // accessing it at the high address is like using any other + // area of general-purpose DRAM. + // + // thus, during NT, the shadowed copy will be overwritten with + // random data as the pages are used, and we must warn the BIOS + // to recopy the ROM into the shadowed RAM on bootup. Here's + // the magic to instruct the BIOS accordingly: + // + // Note that once RRD regains control, he will again + // shadow the BIOS as long as the EISA Config CMOS bits + // instruct him to. The shadowing is only being disabled + // here because the BIOS will initially get control _before_ + // the Corollary RRD ROM does. + // + // if this code wasn't here, the user would have to cycle + // power to boot up successfully. + // + BiosProcessorCSR = (PUCHAR)(KeGetPcr()->HalReserved[PCR_CSR]); + + *(BiosProcessorCSR + CBUS1_SHADOW_REGISTER) = + DISABLE_BIOS_SHADOWING; + + // + // reset the keyboard controller. + // + WRITE_PORT_UCHAR(KEYBPORT, RESET); +loop: + goto loop; +} + +// +// +// most internal Cbus1 support routines begin here. +// +// the exception is the internal routines needed for booting, +// which must be adjacent to the externally-visible boot +// routine above. see the above comments for more details. +// +// +// + +VOID +Cbus1ECCEnable(VOID) +/*++ + +Routine Description: + + Enable Cbus1 ECC detection as ordered by the RRD. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + ULONG Index; + PMEMORY_CARD pm; + + if (CbusGlobal.nonstdecc) + return; + + pm = CbusMemoryBoards; + + for (Index = 0; Index < CbusMemoryBoardIndex; Index++, pm++) { + + if (pm->io_attr) + COUTB(pm->regmap, 0, pm->io_attr); + + } +} + + +VOID +Cbus1ECCDisable(VOID) +/*++ + +Routine Description: + + Disable Cbus1 ECC + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + ULONG Index; + PMEMORY_CARD pm; + + if (CbusGlobal.nonstdecc) + return; + + pm = CbusMemoryBoards; + + for (Index = 0; Index < CbusMemoryBoardIndex; Index++, pm++) { + + if (pm->io_attr) + COUTB(pm->regmap, 0, pm->io_attr & ~CBUS1_EDAC_EN); + } +} + +PUCHAR +CbusIDtoCSR( +IN ULONG ArbID +) +/*++ + +Routine Description: + + Search for the given arbitration ID in the + global extended ID table, then return its CSR. + +Arguments: + + ArbID - Supplies an arbitration ID (that just won the contention cycle) + +Return Value: + + CSR of the specified arbitration ID, 0 if none found + +--*/ +{ + ULONG Index; + PEXT_ID_INFO Idp; + + for ( Index = 0; Index < CbusProcessors; Index++) { + + Idp = (PEXT_ID_INFO)CbusCSR[Index].idp; + + if (Idp->id == ArbID) { + + return (PUCHAR)CbusCSR[Index].csr; + + } + } + + return (PUCHAR)0; +} + + +VOID +Cbus1Arbitrate(VOID) +/*++ + +Routine Description: + + Called by the boot processor to arbitrate the other processors + out of reset, and brand them with IDs. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + ULONG Index, ArbID; + PUCHAR csr, ArbCSR; + ULONG AdditionalProcessors = 0; + extern PVOID Cbus1FindDeadID( ULONG ); + + // + // issue a global broadcast to put all additional processors into reset. + // + + csr = (PUCHAR)CbusBroadcastCSR + CbusGlobal.smp_sreset; + *((PULONG)csr) = CbusGlobal.smp_sreset_val; + + // + // access ID 0's C-bus I/O space - this is different from the boot CPU. + // + for (Index = 0; Index < CbusGlobal.broadcast_id; Index++) { + + COUTB(Cbus1ID0CSR, CbusGlobal.smp_contend, + CbusGlobal.smp_contend_val); + + COUTB(CbusBroadcastCSR, CbusGlobal.smp_contend, + CbusGlobal.smp_contend_val); + + + ArbID = READ_PORT_UCHAR(ATB_STATREG) & BS_ARBVALUE; + + ArbCSR = CbusIDtoCSR(ArbID); + + if (ArbCSR) { + COUTB(ArbCSR, CbusGlobal.smp_setida, + CbusGlobal.smp_setida_val); + + if (ArbID >= LOWCPUID && ArbID <= HICPUID) + AdditionalProcessors++; + } + else { + if (!Cbus1FindDeadID(ArbID)) { + CbusHardwareFailure(MSG_ARBITRATE_ID_ERR); + } + } + + ArbID = READ_PORT_UCHAR(ATB_STATREG) & BS_ARBVALUE; + + if (ArbID == 0) { + ASSERT(AdditionalProcessors == CbusProcessors - 1); + return; + } + }; + + CbusHardwareFailure(MSG_ARBITRATE_FAILED); +} + + +VOID +Cbus1HandleJumpers() + +/*++ + +Routine Description: + + "Recover" any low memory which has been repointed at the EISA bus + via EISA config. This typically happens when ISA/EISA cards with + dual-ported memory are in the machine, and memory accesses need + to be pointed at the card, as opposed to C-bus general purpose memory. + The Corollary architecture provides a way to still gain use of the + general purpose memory, as well as be able to use the I/O dual-port + memory, by double mapping the C-bus memory at a high memory address. + + note that memory accesses in the 640K-1MB hole are by default, pointed + at the EISA bus, and don't need to be repointed via EISA config. + + note this is where jumper decoding and memory_holes set up + happens, but the actual memory will not be released (and + hence used), until HalInitSystem Phase 0. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + ULONG i, j; + ULONG DoublyMapped; + ULONG Address; + extern RRD_CONFIGURATION_T CbusJumpers; + extern VOID CbusMemoryFree(ULONG, ULONG); + + if (CbusGlobal.useholes == 0) { + return; + } + + // + // if the base of RAM is _zero_ (ie: XM or later), then we + // will recover the holes up above the "top of RAM", so any + // I/O device dual-port RAM at the low address will continue to work. + // + // if the base of RAM is NON-ZERO, then we are on an older + // Corollary system which is not supported by this HAL - the + // standard Windows NT uniprocessor HAL should be used instead. + // + + if (CbusGlobal.baseram != 0) + return; + + DoublyMapped = CbusGlobal.memory_ceiling; + + // + // reclaim the 640K->1MB hole first + // + CbusMemoryFree(DoublyMapped + 640 * 1024, 384 * 1024); + + // + // see if this memory span has been dipswitch + // disabled. if this memory exists (in C-bus + // space and it has been jumpered, add it to + // the list so it will be freed later. + // + for (i = 0; i < ATMB; i++) { + if (CbusJumpers.jmp[i] && CbusJumpers.mem[i]) { + Address = MB(i) + DoublyMapped; + j = 1; + for (i++; i < ATMB && CbusJumpers.jmp[i] && CbusJumpers.mem[i]; i++) + j++; + CbusMemoryFree(Address, MB(j)); + } + } +} + + +VOID +Cbus1ParseRRD( +IN PEXT_ID_INFO Table, +IN OUT PULONG Count +) +/*++ + +Routine Description: + + Check for Cbus1 system boards being up to date for running NT. + Obsolete additional CPU boards are removed from the configuration + structure, and are not booted later. If the boot CPU is obsolete, + the system is halted. + +Arguments: + + Table - Supplies a pointer to the RRD extended ID information table + + Count - Supplies a pointer to the number of valid entries in the + RRD extended ID information table + +Return Value: + + Shuffles the input table, and modifies the count of valid entries, + in order to remove any obsolete processors. + + None. + +--*/ +{ + ULONG Index; + ULONG Length; + ULONG j; + PEXT_ID_INFO p; + PEXT_ID_INFO s; + UCHAR HalObsoleteBoardsMsg[80]; + + Cbus1DeadIDsIndex = 0; + p = Table; + + for (Index = 0; Index < *Count; Index++, p++) { + + // + // check for the ID 0 entry, which must be the first one. + // although this is NOT a processor, it is needed to + // arbitrate all the Cbus1 additional processors and set their + // IDs. so capture it now. + // + + if (Index == 0) { + // + // RRD specifies how much to map in per processor - + // for Cbus1, RRD must give us a size which includes any + // processor-specific registers the HAL may access, + // generally indicated via the CbusGlobal structure. + // + Cbus1ID0CSR = (PUCHAR)HalpMapPhysicalMemoryWriteThrough ( + (PVOID)p->pel_start, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + p->pel_start, p->pel_size)); + + continue; + } + + if (p->pm == 0) + continue; + + if ((p->pel_features & ELEMENT_HAS_APIC) == 0) { + + // + // the boot processor MUST have an APIC + // so that we can support distributed + // interrupts on Cbus1 platforms. + // + if ((UCHAR)p->id == CbusGlobal.bootid) { + FatalError(MSG_OBSOLETE_PIC); + } + + *(PEXT_ID_INFO)&Cbus1DeadIDs[Cbus1DeadIDsIndex] = + *p; + + Cbus1DeadIDsIndex++; + + // + // remove old board from "found" array + // + + s = p; + for (j = Index+1; j < *Count; j++, s++) { + *s = *(s + 1); + } + + } + + } + + if (Cbus1DeadIDsIndex) { + + // + // can't let any disabled processors be found, + // warn the user that we are disabling some number + // of processor boards because they cannot access + // the EISA bus. + // + *Count -= Cbus1DeadIDsIndex; + + sprintf(HalObsoleteBoardsMsg, MSG_OBSOLETE_PROC, Cbus1DeadIDsIndex); + HalDisplayString(HalObsoleteBoardsMsg); + } + + Cbus1HandleJumpers(); + + // + // Inform the rest of the system of the resources the HAL has reserved + // from the base of C-bus I/O to 4GB. + // + Length = 0xffffffff - CbusGlobal.cbusio; + AddMemoryResource(CbusGlobal.cbusio, Length + 1); +} + +PVOID +Cbus1FindDeadID( +IN ULONG ArbID +) +/*++ + +Routine Description: + + Check for a given arbitration ID (that just won) is a "dead" Cbus1 + system board - ie: one that we have software disabled because it is + a processor incapable of symmetric I/O. + +Arguments: + + ArbID - Supplies an arbitration ID to match + +Return Value: + + An opaque pointer to the disabled processor's CSR space, 0 + if the ID was not found. + +--*/ +{ + ULONG Dead; + PUCHAR csr = (PUCHAR)0; + PEXT_ID_INFO Idp = (PEXT_ID_INFO)Cbus1DeadIDs; + extern VOID CbusIOPresent(ULONG, ULONG, ULONG, ULONG, ULONG, PVOID); + + for (Dead = 0; Dead < Cbus1DeadIDsIndex; Dead++, Idp++) { + if (Idp->id != ArbID) { + continue; + } + + csr = (PUCHAR)HalpMapPhysicalMemoryWriteThrough ( + (PVOID)Idp->pel_start, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + Idp->pel_start, Idp->pel_size)); + + COUTB(csr, CbusGlobal.smp_setida, + CbusGlobal.smp_setida_val); + + switch (Idp->io_function) { + case IOF_INVALID_ENTRY: + case IOF_NO_IO: + case IOF_MEMORY: + break; + default: + // + // Add this I/O functionality to our table + // to make available to Cbus hardware drivers. + // + // pel_features wasn't set for the old boards, + // so check io_function instead. + // + + if (Idp->io_function == IOF_CBUS1_SIO || + (Idp->io_function == IOF_CBUS1_SCSI)) { + + // + // Add this I/O card to our list of + // available I/O peripherals. + // + CbusIOPresent( + (ULONG)Idp->id, + (ULONG)Idp->io_function, + (ULONG)Idp->io_attr, + Idp->pel_start, + Idp->pel_size, + (PVOID)csr); + } + break; + } + + } + + return (PVOID)csr; +} + + +/*++ + +Routine Description: + + This function calculates the stall execution, and initializes the + HAL-specific hardware device (CLOCK & PROFILE) interrupts for the + Corollary Cbus1 architecture. + +Arguments: + + Processor - Supplies a logical processor number to initialize + +Return Value: + + VOID + +--*/ + +VOID +Cbus1InitializeInterrupts( +IN ULONG Processor +) +{ + // + // Cbus1: stall uses the APIC to figure it out (needed in phase0). + // the clock uses the APIC (needed in phase0) + // the perfcounter uses irq0 (not needed till all cpus boot) + // the profile uses RTC irq8 (not needed till all cpus boot) + // + // Cbus2: stall uses the RTC irq8 to figure it out (needed in phase0). + // the clock uses the irq0 (needed in phase0) + // the perfcounter uses RTC irq8 (not needed till all cpus boot) + // + + if (Processor == 0) { + // + // we must be at Phase0 of system initialization. we need to + // assign vectors for interrupts needed during Phase0. + // currently only the APIC clock is needed during Phase0. + // + CbusVectorToEoi[CBUS1_CLOCK_TASKPRI] = + CbusApicVectorToEoi(CBUS1_CLOCK_TASKPRI); + } + + // + // Note that for Cbus1, InitializeClock MUST be called after + // HalpInitializeStallExecution, since both program the APIC timer. + // Note that stall execution sets up his own IDT entry for handling + // the incoming interrupt, so only the above EOI setup is needed. + // + + Cbus1InitializeStall(Processor); + + // + // Call the hardware backend handler to generate an + // interrupt every 10 milliseconds to be used as the system timer. + // + + Cbus1InitializeClock(); + + // + // Set up the irq0 performance counter and irq8 profile interrupts. + // + + if (Processor == 0) { + Cbus1SetupPrivateVectors(); + Cbus1InitializePerf(); + } + + // + // enable both the irq0 and irq8 interrupts as well as the APIC clocks. + // This also registers the resources being used by these interrupts + // so other subsystems know the HAL has reserved them. + // + + Cbus1InitializeDeviceIntrs(Processor); + + // + // APC, DPC and IPI have already been initialized and enabled + // as part of HalInitializeProcessor. + // +} + +/*++ + +Routine Description: + + This function initializes the HAL-specific hardware device + (CLOCK & PROFILE) interrupts for the Corollary Cbus1 architecture. + +Arguments: + + none. + +Return Value: + + VOID + +--*/ + +VOID +Cbus1InitializeDeviceIntrs( +IN ULONG Processor +) +{ + extern VOID Cbus1ClockInterrupt(VOID); + + // + // here we initialize & enable all the device interrupts. + // this routine is called from HalInitSystem. + // + // each processor needs to call KiSetHandlerAddressToIDT() + // and HalEnableSystemInterrupt() for himself. + // + + if (Processor == 0) { + + // + // Support the HAL's exported interface to the rest of the + // system for the IDT configuration. This routine will + // also set up the IDT entry and enable the actual interrupt. + // + // Only one processor needs to do this, especially since + // the additional processors are vectoring elsewhere for speed. + // + + HalpEnableInterruptHandler ( + DeviceUsage, // Mark as device vector + CBUS1_CLOCK_TASKPRI, // Bus interrupt level + CBUS1_CLOCK_TASKPRI, // System IDT + CLOCK2_LEVEL, // System Irql + Cbus1ClockInterrupt, // ISR + Latched); + + HalpEnableInterruptHandler ( + DeviceUsage, // Mark as device vector + IRQ8, // Bus interrupt level + CBUS1_PROFILE_TASKPRI, + PROFILE_LEVEL, // System Irql + HalpProfileInterrupt, // ISR + Latched); + + HalpEnableInterruptHandler ( + DeviceUsage, // Mark as device vector + IRQ0, // Bus interrupt level + CBUS1_PERF_TASKPRI, // System IDT + CLOCK2_LEVEL, // System Irql + Cbus1PerfInterrupt, // ISR + Latched); + + } + else { + KiSetHandlerAddressToIDT(CBUS1_CLOCK_TASKPRI, CbusClockInterruptPx); + HalEnableSystemInterrupt(CBUS1_CLOCK_TASKPRI, CLOCK2_LEVEL, Latched); + + KiSetHandlerAddressToIDT(CBUS1_PROFILE_TASKPRI, HalpProfileInterruptPx); + HalEnableSystemInterrupt(CBUS1_PROFILE_TASKPRI, PROFILE_LEVEL, Latched); + } +} + +// +// Mask for valid bits of edge/level control register (ELCR) in 82357 ISP: +// ie: ensure irqlines 0, 1, 2, 8 and 13 are always marked edge, as the +// I/O register will not have them set correctly. All other bits in the +// I/O register will be valid without us having to poke them. +// +#define ELCR_MASK 0xDEF8 + +#define PIC1_ELCR_PORT (PUCHAR)0x4D0 // ISP edge/level control regs +#define PIC2_ELCR_PORT (PUCHAR)0x4D1 + +ULONG +Cbus1QueryInterruptPolarity( +VOID +) +/*++ + +Routine Description: + + Called once to read the EISA interrupt configuration registers. + This will tell us which interrupt lines are level-triggered and + which are edge-triggered. Note that irqlines 0, 1, 2, 8 and 13 + are not valid in the 4D0/4D1 registers and are defaulted to edge. + +Arguments: + + None. + +Return Value: + + The interrupt line polarity of all the EISA irqlines in the system. + +--*/ +{ + ULONG InterruptLines = 0; + + // + // Read the edge-level control register (ELCR) so we'll know how + // to mark each driver's interrupt line (ie: edge or level triggered). + // in the APIC I/O unit redirection table entry. + // + + InterruptLines = ( ((ULONG)READ_PORT_UCHAR(PIC2_ELCR_PORT) << 8) | + ((ULONG)READ_PORT_UCHAR(PIC1_ELCR_PORT)) ); + + // + // Explicitly mark irqlines 0, 1, 2, 8 and 13 as edge. Leave all + // other irqlines at their current register values. + // + + InterruptLines &= ELCR_MASK; + + return InterruptLines; +} diff --git a/private/ntos/nthals/halcbus/i386/cbus1.h b/private/ntos/nthals/halcbus/i386/cbus1.h new file mode 100644 index 000000000..a2ba56077 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus1.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus1.h + +Abstract: + + Cbus1 architecture definitions for the Corollary Cbus1 + multiprocessor HAL modules. + +Author: + + Landy Wang (landy@corollary.com) 05-Oct-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#ifndef _CBUS1_ +#define _CBUS1_ + +// +// +// FIRST, THE CBUS1 HARDWARE ARCHITECTURE DEFINITIONS +// +// + +#define COUTB(addr, reg, val) (((PUCHAR)(addr))[reg] = (UCHAR)val) + +#define CBUS1_CACHE_LINE 16 // 16 byte lines +#define CBUS1_CACHE_SHIFT 4 // byte size to line size +#define CBUS1_CACHE_SIZE 0x100000 // 1 Mb caches + +#define ATB_STATREG (PUCHAR)0xf1 // EISA bridge status register +#define BS_ARBVALUE 0xf // arbitration value + +#define LOWCPUID 0x1 // lowest arbitration slot id +#define HICPUID 0xf // highest arbitration slot id + +#define CBUS1_SHADOW_REGISTER 0xB0 // offset of shadow register + +#define DISABLE_BIOS_SHADOWING 0x0 // disable ROM BIOS shadowing + +// +// defines for the Cbus1 ecc control register +// +#define CBUS1_EDAC_SAEN 0x80 +#define CBUS1_EDAC_1MB 0x40 +#define CBUS1_EDAC_WDIS 0x20 +#define CBUS1_EDAC_EN 0x10 +#define CBUS1_EDAC_SAMASK 0xf + +// +// Physical address of the Cbus1 local APIC +// +#define CBUS1_LOCAL_APIC_LOCATION (PVOID)0xFEE00000 + +// +// Physical address of the Cbus1 I/O APIC +// +#define CBUS1_IO_APIC_LOCATION (PVOID)0xFEE00000 + +#endif // _CBUS1_ diff --git a/private/ntos/nthals/halcbus/i386/cbus1bt.asm b/private/ntos/nthals/halcbus/i386/cbus1bt.asm new file mode 100644 index 000000000..5f36b92ad --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus1bt.asm @@ -0,0 +1,314 @@ + title "MP primitives for the Corollary Cbus machines" +;++ +; +;Copyright (c) 1992, 1993, 1994 Corollary Inc. +; +;Module Name: +; +; cbus1bt.asm +; +;Abstract: +; +; Corollary Cbus1 Boot Code +; +; This module implements the low-level highly cache +; architecture dependent code to boot the additional +; processors in the Corollary Cbus1 based machines. + +; This consists of two functions which are exactly the +; same (Cbus1Boot1 & Cbus1Boot2). The calling code +; determines which one is safe to call (depending on the +; linker, sometimes both may be ok). The reason for this +; is that the boot processor fills in the reset vector at +; 0xFFFFFFF0 for the next processor and that cache line +; must not be inadvertently flushed before the next processor +; gets out of reset to see where to go (it's filled in with +; a real-mode jmp cs:ip). Note that this code is highly +; dependent on the linker placing all this code contiguous +; and the hardware architecture of the Corollary L2 caches. +; unless the system is fully populated, memory will not exist +; at 0xFFFFFFF0. hence, we must ensure that the cacheline is +; not evicted until the processor has done the jump! + + +; the order of Cbus1Boot1, ciboot, and Cbus1Boot2 is critical. +; Cbus1Boot1 and Cbus1Boot2 must be separated by Cbus1BootCPU; +; Cbus1Boot1 must be defined before Cbus1Boot2. +; the size of all three must be less than 4K. + +; WARNING!!! WARNING!!! WARNING!!! + +; do not put any routines between Cbus1Boot1 and Cbus1Boot2. there +; are tricky games being played with the write back caches so +; that StartVector[] does not get flushed. + +; +;Author: +; +; Landy Wang (landy@corollary.com) 23-Jun-1993 +; +;Environment: +; Kernel mode. +; +;-- + + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros + + .list + +INIT SEGMENT DWORD PUBLIC 'CODE' ; Start 32 bit code + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; Cbus1Boot1 ( +; IN ULONG Processor, +; IN PQUAD Dest, +; IN PQUAD Source, +; IN ULONG ResetAddress, +; IN ULONG ResetValue +; ) +; +; +; Routine Description: +; +; Clear reset on the specified logical processor number, setting his +; reset vector to point at the specified code location. The Dest +; generally points at a reset vector, and thus, unless the system is +; fully populated, memory will not exist at that address. hence, we +; must ensure that the cacheline is not evicted until the processor +; has done the jump! +; +; Arguments: +; +; Processor - Supplies a logical processor number +; +; Dest - Supplies the address of the reset vector where the code below +; will go. +; +; Source - Supplies startup code for this processor, currently a 5 byte +; intrasegment jump, ie: "jmp cs:ip" +; +; Note the reset vector length is hardcoded here to 8 bytes. (ie: Dest +; and Source must point at arrays of 8 bytes each). +; +; ResetAddress - Supplies the address to poke to clear reset +; +; ResetValue - Supplies the value to poke to clear reset +; +; Return Value: +; +; None. +;-- + +ProcessorNumber equ dword ptr [ebp+8] ; zero based +Destination equ dword ptr [ebp+12] +Source equ dword ptr [ebp+16] +ResetAddress equ dword ptr [ebp+20] +ResetValue equ dword ptr [ebp+24] + +cPublicProc _Cbus1Boot1 ,5 + push ebp + mov ebp, esp + push ebx + push esi + push edi + + ; + ; set up all variables to be used after the cache line + ; initialization. this is because we want to load up + ; our register variables with these values and avoid + ; memory references. see the comment below. + ; + + mov eax, PCR[PcStallScaleFactor] ; get per microsecond + ; loop count for the processor + + mov ecx, 40 ; 40 microsecond stall + mul ecx ; (eax) = desired loop count + + mov edx, ResetAddress + mov ebx, ResetValue + + mov esi, Source ; point at the source code + + mov ecx, dword ptr [esi] ; get first dword into a reg + mov esi, dword ptr [esi+4] ; and 2nd dword into a reg + + mov edi, Destination + + ; + ; now start filling in the cache line for the processor coming out + ; of reset. no memory references which may flush this cache line + ; can be made after the below fill UNTIL the booting processor + ; has read the line. (the only memory references made here in this + ; critical time period is the code fetching, but our caller has + ; already determined that none of the code in this function could + ; cause the cache line to be flushed). + ; + + mov dword ptr [edi], ecx ; 1st dword now in the cacheline + mov dword ptr [edi+4], esi ; and 2nd dword now in + + ; + ; cache line is initialized, we must let it get flushed now, or + ; the additional processor will fly blind. + ; + + mov byte ptr [edx], bl ; clear reset + + ; + ; wait approximately 40 microseconds, but don't call + ; KeStallExecutionProcessor() as this might flush the + ; cache line prematurely. inline the function instead. + ; + + align 4 +@@: + sub eax, 1 ; (eax) = (eax) - 1 + jnz short @b + + + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + stdRET _Cbus1Boot1 + +stdENDP _Cbus1Boot1 + + ; + ; force enough spacing between the two boot functions so + ; that at least one of them will always be safe to call. + ; currently that would be 16 bytes (the current cache line + ; size), but make it bigger so any of our OEMs will be safe + ; even if they modify the size of the cache line. + ; + + public _Cbus1Boot1End +_Cbus1Boot1End label byte + db 64 dup (?) + +;++ +; +; VOID +; Cbus1Boot2 ( +; IN ULONG Processor, +; IN PQUAD Dest, +; IN PQUAD Source, +; IN ULONG ResetAddress, +; IN ULONG ResetValue +; ) +; +; +; Routine Description: +; +; Clear reset on the specified logical processor number, setting his +; reset vector to point at the specified code location. The Dest +; generally points at a reset vector, and thus, unless the system is +; fully populated, memory will not exist at that address. hence, we +; must ensure that the cacheline is not evicted until the processor +; has done the jump! +; +; Arguments: +; +; Processor - Supplies a logical processor number +; +; Dest - Supplies the address of the reset vector where the code below +; will go. +; +; Source - Supplies startup code for this processor, currently a 5 byte +; intrasegment jump, ie: "jmp cs:ip" +; +; Note the reset vector length is hardcoded here to 8 bytes. (ie: Dest +; and Source must point at arrays of 8 bytes each). +; +; ResetAddress - Supplies the address to poke to clear reset +; +; ResetValue - Supplies the value to poke to clear reset +; +; Return Value: +; +; None. +;-- + +cPublicProc _Cbus1Boot2 ,5 + push ebp + mov ebp, esp + push ebx + push esi + push edi + + ; + ; set up all variables to be used after the cache line + ; initialization. this is because we want to load up + ; our register variables with these values and avoid + ; memory references. see the comment below. + ; + + mov eax, PCR[PcStallScaleFactor] ; get per microsecond + ; loop count for the processor + + mov ecx, 40 ; 40 microsecond stall + mul ecx ; (eax) = desired loop count + + mov edx, ResetAddress + mov ebx, ResetValue + + mov esi, Source ; point at the source code + + mov ecx, dword ptr [esi] ; get first dword into a reg + mov esi, dword ptr [esi+4] ; and 2nd dword into a reg + + mov edi, Destination + + ; + ; now start filling in the cache line for the processor coming out + ; of reset. no memory references which may flush this cache line + ; can be made after the below fill UNTIL the booting processor + ; has read the line. (the only memory references made here in this + ; critical time period is the code fetching, but our caller has + ; already determined that none of the code in this function could + ; cause the cache line to be flushed). + ; + + mov dword ptr [edi], ecx ; 1st dword now in the cacheline + mov dword ptr [edi+4], esi ; and 2nd dword now in + + ; + ; cache line is initialized, we must let it get flushed now, or + ; the additional processor will fly blind. + ; + + mov byte ptr [edx], bl ; clear reset + + ; + ; wait approximately 40 microseconds, but don't call + ; KeStallExecutionProcessor() as this might flush the + ; cache line prematurely. inline the function instead. + ; + + align 4 +@@: + sub eax, 1 ; (eax) = (eax) - 1 + jnz short @b + + + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + stdRET _Cbus1Boot2 + +stdENDP _Cbus1Boot2 + +INIT ends ; end 32 bit code + end diff --git a/private/ntos/nthals/halcbus/i386/cbus2.c b/private/ntos/nthals/halcbus/i386/cbus2.c new file mode 100644 index 000000000..82ab6278a --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus2.c @@ -0,0 +1,4170 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus2.c + +Abstract: + + This module implements the Corollary Cbus2 specific functions that + define the Hardware Architecture Layer (HAL) for Windows NT. + + Cbus2 architecture includes a 400MB/sec processor-memory bus, as well + as multiple EISA and PCI buses. Up to 10 Pentium processors are supported + in Cbus2, as well as multiple native Cbus2 I/O cards (ie: SCSI, FDDI, VGA, + SIO, etc). Cbus2 is fully symmetric: all processors can reach all + memory and I/O devices, and similarly, all memory and I/O devices can + reach all processors. The CBC supports fully distributed, lowest in group + interrupts, as well as broadcast capabilities. Each Cbus2 processor has + an internal processor write back cache, as well as up to 2MB of L2 direct + mapped write back cache and a fully associative L3 write back victim cache. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Notes: + + Open Issues: + + - Should move the task priority from an fs segment to a ds segment + now that CBC revision2 will have the identity maps. + + - The multiple I/O bus code is complete and ready for testing. + + - The code that supports Cbus2 systems using APICs (local units on + processors and I/O units on both EISA and CBC I/O cards) + is complete and ready for testing. + + - Support for a variable number of Cbus2 CBC hardware interrupt maps + still needs to be coded. + + - Cbus2 ECC enable, disable and handler code is not fleshed out. + when this is done, HandleNMI needs to be fixed for + xx_PORT_UCHAR multiple bridge issues. + +Revision History: + + +--*/ + +#include "halp.h" +#include "pci.h" +#include "pcip.h" +#include "cbus.h" // Cbus1 & Cbus2 max number of elements is here +#include "cbusrrd.h" // HAL <-> RRD interface definitions +#include "cbus2.h" // Cbus2 hardware architecture stuff +#include "cbus_nt.h" // Cbus NT-specific implementation stuff +#include "cbusnls.h" // Cbus error messages +#include "cbusapic.h" // Cbus APIC generic definitions +#include "stdio.h" + +PULONG +CbusApicVectorToEoi( +IN ULONG Vector +); + +VOID +Cbus2BootCPU ( +IN ULONG Processor, +IN ULONG StartAddress +); + +VOID +Cbus2InitInterruptPolarity(VOID); + +PVOID +Cbus2LinkVector( +IN PBUS_HANDLER RootHandler, +IN ULONG Vector, +IN ULONG Irqline +); + +ULONG +Cbus2ReadCSR(PULONG); + +VOID +Cbus2WriteCSR(PULONG, ULONG); + +VOID +Cbus2InitializeStall(IN ULONG); + +VOID +Cbus2InitializeClock(VOID); + +VOID +FatalError( +IN PUCHAR ErrorString +); + +VOID +HalpIpiHandler( VOID ); + +VOID +Cbus2InitializeCBC( +IN ULONG Processor +); + +VOID +Cbus2DisableMyInterrupts( ULONG ); + +VOID +HalpSpuriousInterrupt(VOID); + +NTSTATUS +HalpAdjustEisaResourceList ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList + ); + +ULONG +HalpGetEisaInterruptVector ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +ULONG +HalpNoBusData ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +HalpGetEisaData ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +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 +CbusRebootHandler( VOID ); + +VOID +Cbus2InitializeApic( +IN ULONG Processor +); + +VOID +Cbus2CheckBusRanges(VOID); + +ULONG +Cbus2MapBusNumberToBridgeIndex( +ULONG RootBusNumber +); + +VOID +Cbus2SetupPrivateVectors(VOID); + +VOID +Cbus2InitializePlatform(VOID); + +VOID +Cbus2InitializeCPU( +IN ULONG Processor +); + +VOID +Cbus2InitOtherBuses(VOID); + +VOID +Cbus2ParseRRD( +IN PEXT_ID_INFO Table, +IN OUT PULONG Count +); + +VOID +Cbus2InitializeDeviceIntrs( +IN ULONG Processor +); + +UCHAR +Cbus2CheckForPeleSystem( +VOID +); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, Cbus2SetupPrivateVectors) +#pragma alloc_text(INIT, Cbus2BootCPU) +#pragma alloc_text(INIT, Cbus2InitializePlatform) +#pragma alloc_text(INIT, Cbus2InitializeCBC) +#pragma alloc_text(INIT, Cbus2InitializeApic) +#pragma alloc_text(INIT, Cbus2InitializeCPU) +#pragma alloc_text(INIT, Cbus2InitOtherBuses) +#pragma alloc_text(INIT, Cbus2ParseRRD) +#pragma alloc_text(INIT, Cbus2InitializeDeviceIntrs) +#pragma alloc_text(INIT, Cbus2InitInterruptPolarity) +#endif + +VOID +Cbus2DisableMyInterrupts(ULONG); + +extern ADDRESS_USAGE HalpCbusMemoryHole; +extern ULONG CbusMemoryHoleIndex; +extern ADDRESS_USAGE HalpCbusMemoryResource; +extern ULONG CbusMemoryResourceIndex; + +extern MEMORY_ALLOCATION_DESCRIPTOR HalpMemory[]; +extern ULONG HalpMemoryIndex; + +extern RRD_CONFIGURATION_T CbusJumpers; + +extern ULONG CbusRedirVector; +extern ULONG CbusRebootVector; + +extern WCHAR rgzMultiFunctionAdapter[]; +extern WCHAR rgzConfigurationData[]; +extern WCHAR rgzIdentifier[]; + +VOID +CbusPreparePhase0Interrupts( +IN ULONG, +IN ULONG, +IN PVOID +); + +ULONG +Cbus2GetInterruptVector( +IN PBUS_HANDLER BusHandler, +IN PBUS_HANDLER RootHandler, +IN ULONG BusInterruptLevel, +IN ULONG BusInterruptVector, +OUT PKIRQL Irql, +OUT PKAFFINITY Affinity +); + +ULONG Cbus2BridgesFound; +PCSR Cbus2BridgeCSR[CBUS_MAX_BRIDGES]; +UCHAR Cbus2PeleSystemFound; + +PCSR Cbus2IdentityCSR; + +PHYSICAL_ADDRESS Cbus2BridgeCSRPaddr[CBUS_MAX_BRIDGES]; +ULONG Cbus2BridgeId[CBUS_MAX_BRIDGES]; +ULONG Cbus2IrqPolarity[CBUS_MAX_BRIDGES]; +ULONG Cbus2BridgePCIBusNumber[CBUS_MAX_BRIDGES]; +ULONG Cbus2BridgeOverride; + +ULONG Cbus2InterruptController; + +ULONG Cbus2FixLevelInterrupts; +ULONG Cbus2CheckSpuriousClock; +ULONG Cbus2EnableBroadcast; +ULONG Cbus2ClockVector; +ULONG Cbus2EoiToHwmap; +UCHAR Cbus2InterruptPolarity[MAXIMUM_IDTVECTOR + 1]; +PULONG Cbus2TimeStamp0; + +extern PULONG CbusTimeStamp; +extern PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1]; +extern ULONG CbusVectorToHwmap[MAXIMUM_IDTVECTOR + 1]; + +// +// Created for HalRequestIpi streamlining when operating in CBC mode - +// otherwise the ipi address could always have been gotten via CbusCSR[]. +// +PULONG Cbus2SendIPI[MAX_CBUS_ELEMENTS]; +PULONG Cbus2Poke8042; + +KSPIN_LOCK Cbus2NMILock; + +// +// for Cbus2, the CBC interrupt hardware supports all 256 interrupt +// priorities, and unlike the APIC, doesn't disable receipt of interrupts +// at granularities of 16-deep buckets. instead the CBC uses the whole byte, +// instead of 4 bits like the APIC, giving us a granularity of 1. This +// fine granularity results in us having more available priorities than +// both NT's 32 IRQL levels _AND_ the total number of EISA irq lines. +// This distinction is critical because it allows us to associate a unique +// vector AND PRIORITY to every single interrupt. +// + + +// +// our interrupt prioritization picture from lowest priority +// to highest priority) thus looks as follows: +// +// APC: 0x1F (lowest priority) +// DPC: 0x2F +// Lowest Priority EISA: 0x30 +// Highest Priority EISA: 0x4F +// +// unused vectors: 0x50->0x60 +// +// Lowest Priority CBC: 0x61 +// Highest Priority CBC: 0xE9 +// EISA Profile: 0xEB +// EISA Clock: 0xEC +// Hal Private Reboot IPI: 0xED +// IPI Broadcasting: 0xEE->FC +// IPI: 0xFD +// Power: 0xFE +// High: 0xFE (highest priority) +// Spurious: 0xFF (highest priority) +// +// + +#define CBUS2_PROFILE_TASKPRI 0xEB +#define CBUS2_CLOCK_TASKPRI 0xEC + +// +// all interrupts from this vector up can be enabled by just setting up +// the calling processor's interrupt configuration register in his CBC. +// + +#define CBUS2_REBOOT_TASKPRI 0xED +#define CBUS2_REDIR_IPI 0xEE +#define CBUS2_ALTERNATE_IPI 0xEF + +// +// It's OK if the redirection IPI shares a vector assignment with the +// broadcast_low IPI - because only one of them will be used in a given +// system - the redirection IPI will only be used if we are using APICs to +// communicate; the broadcast_low IPI will only be used if we are using +// CBCs to communicate - we will never use both in the same system. +// +#define CBUS2_BROADCAST_TASKPRI_LOW 0xEE // only used by Cbus2 CBC +#define CBUS2_BROADCAST_TASKPRI_HIGH 0xFC // only used by Cbus2 CBC + +#define CBUS2_IPI_TASKPRI 0xFD + +#define CBUS2_POWER_TASKPRI 0xFE + +// +// we don't really care what this value is for the CBC, but the APIC +// spec seems to imply that this must be hardcoded at 0xff for future +// compatibility. +// +#define CBUS2_SPURIOUS_TASKPRI 0xFF + +#define LOWEST_DEVICE_TASKPRI 0x30 +#define LOWEST_CBC_TASKPRI 0x61 +#define HIGHEST_DEVICE_TASKPRI (CBUS2_PROFILE_TASKPRI - 1) // 0xEA + +// +// declare an EISA_IRQ2PRI just to flesh out arrays that will be indexed by +// irq line. the EISA_IRQ2PRI should never actually be used +// + +#define EISA_IRQ2PRI HIGHEST_DEVICE_TASKPRI + +#define EISA_IRQ1PRI (LOWEST_DEVICE_TASKPRI + 0xE * CBUS_MAX_BRIDGES) +#define EISA_IRQ3PRI (LOWEST_DEVICE_TASKPRI + 0xC * CBUS_MAX_BRIDGES) +#define EISA_IRQ4PRI (LOWEST_DEVICE_TASKPRI + 0xB * CBUS_MAX_BRIDGES) +#define EISA_IRQ5PRI (LOWEST_DEVICE_TASKPRI + 0xA * CBUS_MAX_BRIDGES) +#define EISA_IRQ6PRI (LOWEST_DEVICE_TASKPRI + 0x9 * CBUS_MAX_BRIDGES) +#define EISA_IRQ7PRI (LOWEST_DEVICE_TASKPRI + 0x8 * CBUS_MAX_BRIDGES) +#define EISA_IRQ9PRI (LOWEST_DEVICE_TASKPRI + 0x6 * CBUS_MAX_BRIDGES) +#define EISA_IRQAPRI (LOWEST_DEVICE_TASKPRI + 0x5 * CBUS_MAX_BRIDGES) +#define EISA_IRQBPRI (LOWEST_DEVICE_TASKPRI + 0x4 * CBUS_MAX_BRIDGES) +#define EISA_IRQCPRI (LOWEST_DEVICE_TASKPRI + 0x3 * CBUS_MAX_BRIDGES) +#define EISA_IRQDPRI (LOWEST_DEVICE_TASKPRI + 0x2 * CBUS_MAX_BRIDGES) +#define EISA_IRQEPRI (LOWEST_DEVICE_TASKPRI + 0x1 * CBUS_MAX_BRIDGES) +#define EISA_IRQFPRI (LOWEST_DEVICE_TASKPRI) + +#define EISA_IRQ0PRI2 (LOWEST_DEVICE_TASKPRI + 0xF * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ1PRI2 (LOWEST_DEVICE_TASKPRI + 0xE * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ2PRI2 (LOWEST_DEVICE_TASKPRI + 0xD * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ3PRI2 (LOWEST_DEVICE_TASKPRI + 0xC * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ4PRI2 (LOWEST_DEVICE_TASKPRI + 0xB * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ5PRI2 (LOWEST_DEVICE_TASKPRI + 0xA * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ6PRI2 (LOWEST_DEVICE_TASKPRI + 0x9 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ7PRI2 (LOWEST_DEVICE_TASKPRI + 0x8 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ8PRI2 (LOWEST_DEVICE_TASKPRI + 0x7 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQ9PRI2 (LOWEST_DEVICE_TASKPRI + 0x6 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQAPRI2 (LOWEST_DEVICE_TASKPRI + 0x5 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQBPRI2 (LOWEST_DEVICE_TASKPRI + 0x4 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQCPRI2 (LOWEST_DEVICE_TASKPRI + 0x3 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQDPRI2 (LOWEST_DEVICE_TASKPRI + 0x2 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQEPRI2 (LOWEST_DEVICE_TASKPRI + 0x1 * CBUS_MAX_BRIDGES + 1) +#define EISA_IRQFPRI2 (LOWEST_DEVICE_TASKPRI + 1) + +// +// you would think EISA_IRQ0PRI would be equivalent to +// (LOWEST_DEVICE_TASKPRI + 0x1 * CBUS_MAX_BRIDGES - 1). +// however irq0 is the system clock and is instead set to CLOCK2_TASKPRI. +// +// EISA_IRQ8PRI (irq8) is the profile interrupt, and is also +// special-cased in a similar fashion. +// + +#define EISA_IRQ0PRI CBUS2_CLOCK_TASKPRI +#define EISA_IRQ8PRI CBUS2_PROFILE_TASKPRI + +#define UNUSED_PRI HIGHEST_DEVICE_TASKPRI + +// +// 186 priorities (0xE9-0x30+1) are available for device hardware. +// Vectors and IRQLs are given out in HalGetInterruptVector(). Since +// drivers can be dynamically linked in at any time, leave +// space so that they can be given roughly the same priorities as in a +// PC-environment. ie: IRQL(EISA irq0) > IRQL(EISA irq15), and so on, +// in order to mimic the standard Microsoft uniprocessor HAL. +// +// 23 distinct IRQL levels are left for us (by the kernel) to give to +// various hardware devices. Multiple drivers will be allowed to share +// any given IRQL level, although they will be given different VECTORS. +// +// All native Cbus2 I/O will be given priorities higher than that +// of any EISA device. This is because Cbus2 drivers will generally +// be written by Corollary, and thus guaranteed to have short ISRs, fully +// utilizing the DPC mechanism. +// +// The high 8 IRQL levels are reserved for native Cbus2 drivers. +// the 16 hardware interrupts for each Cbus2 CBC will be given IRQL +// priority on a "lower local irq line gets higher priority IRQL" basis. +// +// The low 15 IRQLs are for EISA devices, with +// EISA irql == EISA irq + LOWEST_DEVICE_TASKPRI. +// EISA irq lines on each bus are assigned identical IRQLs. +// irq2 (slave 8259), irq0 (clock) and irq8 (profile) are +// included in these calculations despite the fact that irq2 can't happen, +// and irq0/irq8 are assigned special (higher) priorities above. +// They are included because multiple I/O buses are a feature of +// Cbus2, and the irq0/irq8 of the second I/O bus could be wired +// up to devices other than 8254 or RTC clocks. +// + +#define EISA_IRQLS 15 + +#define EISA_LOW_IRQL (DISPATCH_LEVEL + 1) +#define EISA_HIGH_IRQL (EISA_LOW_IRQL + EISA_IRQLS - 1) +#define CBC_HIGH_IRQL (PROFILE_LEVEL - 1) + +// +// Limit to 9 I/O CBCs for now: 154 (0xe9 - 0x50 + 1) vectors are available +// after the dual EISAs and kernel vectors are assigned. Divide this up +// amongst 16 hardware interrupt map entries per CBC, and 9 is what you get. +// Note that to allow more than 9 active I/O CBCs, they cannot all +// be using all their interrupt lines, since there are not enough IDT +// entries to give them all unique vectors. +// +// This limitation should never be a problem, as it is generally +// unlikely that more than a few interrupt lines per Cbus2 native I/O CBC will +// be used. +// +#define NCBC_BUCKETS 9 + +// +// each pair of adjacent Cbus2 CBC irqlines on all CBCs will be mapped +// to the same IRQL. ie: CBC 0..n will have IRQL 27 assigned for hardware +// interrupt map entries 0 & 1 on these CBCs. This is because after EISA +// Irql assignments, there are only 8 Irql levels left, and 16 +// CBC hardware interrupt lines can request vectors and levels. +// +#define CBC_IRQL_GROUPING 2 // each pair of lines shares an Irql + +#define HIGHEST_CBC_TASKPRI HIGHEST_DEVICE_TASKPRI // 0xEA + +#if (HIGH_LEVEL + 1 != 32) +Cause error to get attention: +Cbus2IrqlToVector[] must be built and indexed properly +#endif + +#if DBG +#define MAX_ELEMENT_CSRS 15 + +#if (CBUS2_BROADCAST_TASKPRI_HIGH - CBUS2_BROADCAST_TASKPRI_LOW + 1 != MAX_ELEMENT_CSRS) +cause error to get attention - above task priority assignment must +leave a broadcast priority for each potential Cbus2 processor in the system. +this set of priorities MUST be higher than CLOCK2, _AND_ less than IPI, +so that IPI_IRQL/IPI_TASKPRI will be sufficient to block any of the +broadcast priorities. +#endif +#endif + +// +// since each Irql will hold CBUS_MAX_BRIDGES lines at that priority, ensure +// that the Cbus2 Irql array masks off all lines at a given Irql priority... +// +#define MAX_EISA_PRIORITY(eisa_taskpri) (eisa_taskpri + CBUS_MAX_BRIDGES - 1) + +#define MAX_CBC_PRIORITY(cbc_taskpri) \ + (cbc_taskpri + CBC_IRQL_GROUPING * NCBC_BUCKETS - 1) + +ULONG Cbus2IrqlToVector[HIGH_LEVEL + 1 ] = { + + LOW_TASKPRI, + APC_TASKPRI, + DPC_TASKPRI, + MAX_EISA_PRIORITY(EISA_IRQFPRI), + + MAX_EISA_PRIORITY(EISA_IRQEPRI), + MAX_EISA_PRIORITY(EISA_IRQDPRI), + MAX_EISA_PRIORITY(EISA_IRQCPRI), + MAX_EISA_PRIORITY(EISA_IRQBPRI), + + MAX_EISA_PRIORITY(EISA_IRQAPRI), + MAX_EISA_PRIORITY(EISA_IRQ9PRI), + EISA_IRQ8PRI2, // used on second bridge only + MAX_EISA_PRIORITY(EISA_IRQ7PRI), + + MAX_EISA_PRIORITY(EISA_IRQ6PRI), + MAX_EISA_PRIORITY(EISA_IRQ5PRI), + MAX_EISA_PRIORITY(EISA_IRQ4PRI), + MAX_EISA_PRIORITY(EISA_IRQ3PRI), + + EISA_IRQ2PRI2, // used on second bridge only + MAX_EISA_PRIORITY(EISA_IRQ1PRI), + EISA_IRQ0PRI2, // used on second bridge only + HIGHEST_DEVICE_TASKPRI - 7 * CBC_IRQL_GROUPING * NCBC_BUCKETS, + + HIGHEST_DEVICE_TASKPRI - 6 * CBC_IRQL_GROUPING * NCBC_BUCKETS, + HIGHEST_DEVICE_TASKPRI - 5 * CBC_IRQL_GROUPING * NCBC_BUCKETS, + HIGHEST_DEVICE_TASKPRI - 4 * CBC_IRQL_GROUPING * NCBC_BUCKETS, + HIGHEST_DEVICE_TASKPRI - 3 * CBC_IRQL_GROUPING * NCBC_BUCKETS, + + HIGHEST_DEVICE_TASKPRI - 2 * CBC_IRQL_GROUPING * NCBC_BUCKETS, + HIGHEST_DEVICE_TASKPRI - CBC_IRQL_GROUPING * NCBC_BUCKETS, + HIGHEST_DEVICE_TASKPRI, + CBUS2_PROFILE_TASKPRI, + + CBUS2_CLOCK_TASKPRI, + CBUS2_IPI_TASKPRI, + CBUS2_POWER_TASKPRI, + HIGH_TASKPRI, +}; + +// +// A table converting software interrupt Irqls to Cbus2-specific offsets +// within a given CSR space. Note that all task priorities are shifted +// by the Cbus2 register width (64 bits) to create the correct hardware +// offset to poke to cause the interrupt. This table is declared here to +// optimize the assembly software interrupt request lookup, and is filled +// in as part of InitializePlatform. +// + +#define CBUS2_REGISTER_SHIFT 3 + +// +// Although the IrqlToAddr table is really used only to speed up software +// interrupt dispatching, it is filled in for all possible IRQLs. +// +ULONG Cbus2IrqlToCbus2Addr[HIGH_LEVEL + 1]; + +#if (MAX_EISA_PRIORITY(EISA_IRQ1PRI) >= \ + HIGHEST_DEVICE_TASKPRI - 8 * CBC_IRQL_GROUPING * NCBC_BUCKETS) +Cause error to get attention: Cbus2 & EISA vectors must not overlap +#endif + + +#define EISA_IRQLINES 16 + +typedef struct _cbus2_irqline_t { + ULONG Vector; + KIRQL Irql; +} CBUS2_IRQLINE_T, *PCBUS2_IRQLINE; + +// +// map EISA irqline to Cbus2 programmable vectors & IRQL levels... +// + +CBUS2_IRQLINE_T Cbus2EISAIrqlines[CBUS_MAX_BRIDGES][EISA_IRQLINES] = +{ + EISA_IRQ0PRI, CLOCK2_LEVEL, + EISA_IRQ1PRI, EISA_LOW_IRQL+0xE, // Irql 11 + EISA_IRQ2PRI, PROFILE_LEVEL -1, + EISA_IRQ3PRI, EISA_LOW_IRQL+0xC, // Irql F + + EISA_IRQ4PRI, EISA_LOW_IRQL+0xB, + EISA_IRQ5PRI, EISA_LOW_IRQL+0xA, + EISA_IRQ6PRI, EISA_LOW_IRQL+0x9, + EISA_IRQ7PRI, EISA_LOW_IRQL+0x8, // Irql B + + EISA_IRQ8PRI, PROFILE_LEVEL, + EISA_IRQ9PRI, EISA_LOW_IRQL+6, + EISA_IRQAPRI, EISA_LOW_IRQL+5, + EISA_IRQBPRI, EISA_LOW_IRQL+4, // Irql 7 + + EISA_IRQCPRI, EISA_LOW_IRQL+3, + EISA_IRQDPRI, EISA_LOW_IRQL+2, + EISA_IRQEPRI, EISA_LOW_IRQL+1, + EISA_IRQFPRI, EISA_LOW_IRQL, // Irql 3 + + EISA_IRQ0PRI2, EISA_LOW_IRQL+0xF, // Irql 13 + EISA_IRQ1PRI2, EISA_LOW_IRQL+0xE, + EISA_IRQ2PRI2, EISA_LOW_IRQL+0xD, + EISA_IRQ3PRI2, EISA_LOW_IRQL+0xC, // Irql F + + EISA_IRQ4PRI2, EISA_LOW_IRQL+0xB, + EISA_IRQ5PRI2, EISA_LOW_IRQL+0xA, + EISA_IRQ6PRI2, EISA_LOW_IRQL+0x9, + EISA_IRQ7PRI2, EISA_LOW_IRQL+0x8, // Irql B + + EISA_IRQ8PRI2, EISA_LOW_IRQL+7, + EISA_IRQ9PRI2, EISA_LOW_IRQL+6, + EISA_IRQAPRI2, EISA_LOW_IRQL+5, + EISA_IRQBPRI2, EISA_LOW_IRQL+4, // Irql 7 + + EISA_IRQCPRI2, EISA_LOW_IRQL+3, + EISA_IRQDPRI2, EISA_LOW_IRQL+2, + EISA_IRQEPRI2, EISA_LOW_IRQL+1, + EISA_IRQFPRI2, EISA_LOW_IRQL // Irql 3 +}; + +// +// map Cbus2 hardware interrupt irqlines +// to Cbus2 programmable vectors & IRQL levels... +// + +CBUS2_IRQLINE_T Cbus2CBCIrqlines[REV1_HWINTR_MAP_ENTRIES] = +{ + HIGHEST_CBC_TASKPRI - NCBC_BUCKETS + 1, CBC_HIGH_IRQL, + HIGHEST_CBC_TASKPRI - 2 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL, + HIGHEST_CBC_TASKPRI - 3 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 1, + HIGHEST_CBC_TASKPRI - 4 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 1, + + HIGHEST_CBC_TASKPRI - 5 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 2, + HIGHEST_CBC_TASKPRI - 6 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 2, + HIGHEST_CBC_TASKPRI - 7 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 3, + HIGHEST_CBC_TASKPRI - 8 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 3, + + HIGHEST_CBC_TASKPRI - 9 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 4, + HIGHEST_CBC_TASKPRI -10 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 4, + HIGHEST_CBC_TASKPRI -11 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 5, + HIGHEST_CBC_TASKPRI -12 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 5, + + HIGHEST_CBC_TASKPRI -13 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 6, + HIGHEST_CBC_TASKPRI -14 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 6, + HIGHEST_CBC_TASKPRI -15 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 7, + HIGHEST_CBC_TASKPRI -16 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 7, +}; + +CCHAR HalpFindFirstSetRight[256] = { + 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}; + +VOID +HalpClockInterruptPx( VOID ); + +NTSTATUS +HalpNoAdjustResourceList ( + IN PVOID BusHandler, + IN PVOID RootHandler, + IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList + ); + +NTSTATUS +HalpNoAssignSlotResources ( + IN PVOID BusHandler, + IN PVOID 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 +HalpProfileInterruptPx( VOID ); + +ULONG +HalpGetSystemInterruptVector( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG BusInterruptLevel, + IN ULONG BusInterruptVector, + OUT PKIRQL Irql, + OUT PKAFFINITY Affinity + ); + +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 + ); + +ULONG +Cbus2GetCbusData( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +ULONG +Cbus2SetCbusData( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +// +// defines for resetting the keyboard controller used +// in Cbus2ResetAllOtherProcessors(). +// +#define RESET 0xfe +#define KEYBPORT (PUCHAR )0x64 + +#define BRIDGE0 0 +#define IRQ0 0 +#define IRQ8 8 + +extern ULONG ProfileVector; +extern ULONG CbusClockVector; + +#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; + +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; + +PCI_CONFIG_HANDLER PCIConfigHandler; + +// +// +// Cbus2 switch table entry routines begin here +// +// + + +/*++ + +Routine Description: + + This routine is called only once from HalInitProcessor() at Phase 0 + by the boot cpu. All other cpus are still in reset. + + software(APC, DPC, wake) and IPI vectors have already been initialized + and enabled. + + all we're doing here is setting up some software structures for two + EISA interrupts (clock and profile) so they can be enabled later. + + The bus handler data structures are not initialized until Phase 1, + so HalGetInterruptVector() may not be called before Phase1. + + Hence we cannot pass a valid BusHandler parameter to the Tie code. + That's ok, since it doesn't currently use it. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +VOID +Cbus2SetupPrivateVectors(VOID) +{ + PVOID OpaqueClock; + PVOID OpaqueProfile; + + // + // we are defaulting to the EISA (or MCA) bridge 0 + // for all of these interrupts which need enabling during Phase 0. + // + + // + // unfortunately, we had hardcode the vectors onto bridge 0 - this // is because there is no legitimate bus handler structure to pass + // around at this time during startup. + // + if (Cbus2InterruptController == ELEMENT_HAS_CBC) { + + OpaqueClock = Cbus2LinkVector((PBUS_HANDLER)BRIDGE0, + CbusClockVector, IRQ0); + OpaqueProfile = Cbus2LinkVector((PBUS_HANDLER)BRIDGE0, + ProfileVector, IRQ8); + } + else { + ASSERT (Cbus2InterruptController == ELEMENT_HAS_APIC); + + OpaqueClock = CbusApicLinkVector((PBUS_HANDLER)BRIDGE0, + CbusClockVector, IRQ0); + + OpaqueProfile = CbusApicLinkVector((PBUS_HANDLER)BRIDGE0, + ProfileVector, IRQ8); + } + + CbusPreparePhase0Interrupts(CbusClockVector, IRQ0, OpaqueClock); + CbusPreparePhase0Interrupts(ProfileVector, IRQ8, OpaqueProfile); +} + + +/*++ + +Routine Description: + + This function calculates the stall execution, and initializes the + HAL-specific hardware device (CLOCK & PROFILE) interrupts for the + Corollary Cbus1 architecture. + +Arguments: + + Processor - Supplies a logical processor number to initialize + +Return Value: + + VOID + +--*/ + +VOID +Cbus2InitializeInterrupts( +IN ULONG Processor +) +{ + // + // Cbus2: stall uses the RTC irq8 to figure it out (needed in phase0). + // the clock uses the irq0 (needed in phase0) + // the perfcounter uses RTC irq8 (not needed till all cpus boot) + // + // Cbus1: stall uses the APIC to figure it out (needed in phase0). + // the clock uses the APIC (needed in phase0) + // the perfcounter uses irq0 (not needed till all cpus boot) + // the profile uses RTC irq8 (not needed till all cpus boot) + // + + if (Processor == 0) { + // + // we must be at Phase0 of system initialization. we need to + // assign vectors for interrupts needed during Phase0. + // currently the 8254 clock and the RTC CMOS interrupts are + // needed during Phase0. + // + + Cbus2ClockVector = CbusIrqlToVector[CLOCK2_LEVEL]; + Cbus2SetupPrivateVectors(); + } + + Cbus2InitializeStall(Processor); + + // + // Call the hardware backend handler to generate an + // interrupt every 10 milliseconds to be used as the system timer. + // + + Cbus2InitializeClock(); + + // + // Set up and enable the irq0 performance counter and irq8 profile + // interrupts. Also enable the APIC clocks. This also registers + // the resources being used by these interrupts so other subsystems + // know the HAL has reserved them. + // + + Cbus2InitializeDeviceIntrs(Processor); + + // + // APC, DPC and IPI have already been initialized and enabled + // as part of HalInitializeProcessor. + // +} + +/*++ + +Routine Description: + + Determine if the supplied vector belongs to an EISA device - if so, + then the corresponding bridge's CBC hardware interrupt map entry + will need to be modified to enable the line, so return FALSE here. + + Otherwise, just enable this processor's interrupt configuration register + for the supplied vector and return TRUE immediately. note that for + non-device (software) interrupts, generally no CbusVectorTable[] entries + have been set up. + +Arguments: + + Vector - Supplies a vector number to enable + +Return Value: + + TRUE if the vector was enabled, FALSE if not. + +--*/ +BOOLEAN +Cbus2EnableNonDeviceInterrupt( +IN ULONG Vector +) +{ + PCSR csr; + + // + // if pure software interrupt, setting the calling processor's + // CBC entry to accept the interrupt is sufficient. + // + if (Cbus2InterruptController == ELEMENT_HAS_APIC) { + // + // If pure software interrupt, no action needed. + // Note both APIC timer interrupts and IPIs are + // treated as "software" interrupts. The EOI + // address still needs to be set up here. + // + if (Vector != CBUS2_SPURIOUS_TASKPRI) + CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector); + + if (Vector < LOWEST_DEVICE_TASKPRI || Vector > CBUS2_CLOCK_TASKPRI) { + return TRUE; + } + + // + // indicate that Enable_Device_Interrupt will need to be run + // in order to enable this vector, as it associated with an I/O + // bus device which will need enabling within a locked critical + // section. + // + + return FALSE; + } + + // + // the spurious vector doesn't need any interrupt configuration mucking + // + if (Vector == CBUS2_SPURIOUS_TASKPRI) { + return TRUE; + } + + csr = (PCSR)KeGetPcr()->HalReserved[PCR_CSR]; + + if (Vector < LOWEST_DEVICE_TASKPRI) { + csr->InterruptConfiguration[Vector].csr_register = + HW_IMODE_ALLINGROUP; + + return TRUE; + } + + // + // if just IPI, setting the calling processor's + // CBC entry to accept the interrupt is sufficient. + // + + if (Vector >= CBUS2_REBOOT_TASKPRI && Vector <= CBUS2_IPI_TASKPRI) { + csr->InterruptConfiguration[Vector].csr_register = + HW_IMODE_ALLINGROUP; + return TRUE; + } + + // + // indicate that EnableDeviceInterrupt will need to be run + // in order to enable this vector, as it is associated with an I/O + // bus device which will need enabling within a locked critical + // section. + // + + return FALSE; +} + + +/*++ + +Routine Description: + + Enable the specified interrupt for the calling processor. + Note that the caller holds the HAL's CbusVectorLock at CLOCK_LEVEL + on entry. + +Arguments: + + Vector - Supplies a vector number to enable + + HardwarePtr - Supplies a CSR address on some bridge or native Cbus2 I/O + card which will be generating the specified interrupt vector. + + FirstAttach - TRUE if this is the first processor to enable the + specified vector + + BusNumber - the bus number of the particular bustype. + + Irqline - the irqline on the particular CBC that will be generating this + vector + +Return Value: + + None. + +--*/ +VOID +Cbus2EnableDeviceInterrupt( +IN ULONG Vector, +IN PVOID HardwarePtr, +IN ULONG FirstAttach, +IN USHORT BusNumber, +IN USHORT Irqline +) +{ + PHWINTRMAP hwentry; // CBC entry generating the intr + PCSR io_csr; // CBC entry generating the intr + PCSR csr; + ULONG IrqPolarity; + ULONG BusBridge; + BOOLEAN LowestInGroup; + BOOLEAN LevelTriggered; + + // + // All interrupts are lowest-in-group. However, + // clocks, profiling & IPIs must stay ALL-IN-GROUP. + // + + switch (Vector) { + + case CBUS2_CLOCK_TASKPRI: + case CBUS2_PROFILE_TASKPRI: + case CBUS2_POWER_TASKPRI: + LowestInGroup = FALSE; + break; + + default: + // + // Only enable CBC LIG arbitration if we have more than + // one processor. This is an optimization for speed. + // + if (CbusProcessors > 1) { + LowestInGroup = TRUE; + } + else { + LowestInGroup = FALSE; + } + break; + } + + // + // if we are enabling an Irqline on an SPP, then decrement + // the Irqline first. we artificially set the interrupt range + // to 1-4 from 0-3. there was generic HAL code that did not + // allow for allocation of 0. + // + BusBridge = Cbus2MapBusNumberToBridgeIndex(BusNumber); + if (BusBridge) { + Irqline--; + } + + // + // if this is the _first_ processor to actually enable this + // interrupt, then we'll need to know what kind of interrupt it is. + // + if (Vector >= LOWEST_CBC_TASKPRI && Vector <= HIGHEST_CBC_TASKPRI) { + + ASSERT ((Vector >= EISA_IRQ1PRI + CBUS_MAX_BRIDGES) && + Vector != EISA_IRQ0PRI && + Vector != EISA_IRQ8PRI); + // + // default for Cbus2 I/O cards is edge + // triggered (ie rising edge CBC). + // + LevelTriggered = FALSE; + } + else { + ASSERT ((Vector >= EISA_IRQFPRI && + Vector < EISA_IRQ1PRI + CBUS_MAX_BRIDGES) || + Vector == EISA_IRQ0PRI || + Vector == EISA_IRQ8PRI); + // + // must be an EISA interrupt, so + // get the irq polarity for the specified bus... + // + ASSERT (Irqline < EISA_IRQLINES); + + IrqPolarity=Cbus2IrqPolarity[BusBridge]; + + // + // Mark the caller's interrupt as falling + // (level) or rising (edge) triggered, based + // on the ELCR register we read earlier. + // + if (((IrqPolarity >> Irqline)) & 0x1) { + // + // if level triggered, we must program + // the CBC as falling edge. + // + LevelTriggered = TRUE; + } + else { + // + // if edge triggered, we must program + // the CBC as rising edge. + // + LevelTriggered = FALSE; + } + } + + if (Cbus2InterruptController == ELEMENT_HAS_APIC) { + // + // The EOI address is set up here. + // + CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector); + + CbusEnableApicInterrupt(BusNumber, Vector, HardwarePtr, + FirstAttach, LowestInGroup, LevelTriggered); + return; + } + + // + // Record the interrupt polarity so that the CBUS_EOI macro + // can more easily determine if the level-triggered hardware + // workaround for CBC-2 is required. + // + if (LevelTriggered == TRUE) { + Cbus2InterruptPolarity[Vector]=CBUS2_LEVEL_TRIGGERED_INTERRUPT; + } + else { + Cbus2InterruptPolarity[Vector] = CBUS2_EDGE_TRIGGERED_INTERRUPT; + } + + // + // Set up the processor side of the interrupt initialization. + // this needs to be done for ALL interrupts (ie: software, + // IPI, etc, as well as for real hardware devices). + // note that this must precede the I/O side of the initialization + // because an interrupt could be pending at initialization time + // (especially with a level-triggered interrupt) and the broadcast + // could occur before the processor side is set up. + // this would cause the interrupt to be missed with no way to EOI. + // + csr = (PCSR)KeGetPcr()->HalReserved[PCR_CSR]; + + // + // before allowing LIG interrupts, check to see that + // RRD has configured the system for LIG's. CBC-1 and + // CBC-2 did not support LIG. + // + if (LowestInGroup == TRUE) { + // + // allow OEMs to disable LIG from their ROMs... + // if they do this, each normally LIG device interrupt + // will go only to the first processor to enable it. + // + if (CbusGlobal.Cbus2Features & CBUS_ENABLED_LIG) { + csr->InterruptConfiguration[Vector].csr_register = + HW_IMODE_LIG; + } + else if (FirstAttach) { + csr->InterruptConfiguration[Vector].csr_register = + HW_IMODE_ALLINGROUP; + } + } + else { + csr->InterruptConfiguration[Vector].csr_register = + HW_IMODE_ALLINGROUP; + } + + if (FirstAttach) { + + // + // initialize the generating bridge or Cbus2 native card's CBC + // that will be generating this interrupt. + // + io_csr = (PCSR)HardwarePtr; + hwentry = &io_csr->HardwareInterruptMap[Irqline]; + + // + // Set up the EOI address now that we know which + // bridge's CBC will need it. + // + CbusVectorToEoi[Vector] = (PULONG) + &io_csr->HardwareInterruptMapEoi[Irqline]; + + // + // let the generating CBC know in a single dword access... + // + + if (LevelTriggered == TRUE) { + Cbus2EoiToHwmap = + (int)Cbus2BridgeCSR[BRIDGE0]->HardwareInterruptMapEoi - + (int)Cbus2BridgeCSR[BRIDGE0]->HardwareInterruptMap; + CbusVectorToHwmap[Vector] = (HW_LEVEL_LOW | Vector); + hwentry->csr_register = (HW_LEVEL_LOW | Vector); + } + else { + CbusVectorToHwmap[Vector] = (HW_EDGE_RISING | Vector); + hwentry->csr_register = (HW_EDGE_RISING | Vector); + } + } +} + +/*++ + +Routine Description: + + Disable the specified interrupt so it can not occur on the calling + processor upon return from this routine. Note that the caller holds + the HAL's CbusVectorLock at CLOCK_LEVEL on entry. + +Arguments: + + Vector - Supplies a vector number to disable + + HardwarePtr - Supplies a hardware interrupt map entry address on + the CBC of the bridge whose Vector is specified + + LastDetach - TRUE if this is the last processor to detach from the + specified vector + + Irqline - the irqline this vector is coming in on + +Return Value: + + None. + +--*/ +VOID +Cbus2DisableInterrupt( +IN ULONG Vector, +IN PVOID HardwarePtr, +IN ULONG LastDetach, +IN USHORT BusNumber, +IN USHORT Irqline +) +{ + PHWINTRMAP hwentry; // CBC entry generating the intr + PCSR csr; + + if (Cbus2InterruptController == ELEMENT_HAS_APIC) { + CbusDisableApicInterrupt(BusNumber, Vector, HardwarePtr, + LastDetach); + return; + } + + // + // Only the vector matters to us, irql is irrelevant. + // tell the world that _this processor_ is no longer + // participating in receipt of this interrupt. + // + + csr = (PCSR)(KeGetPcr()->HalReserved[PCR_CSR]); + csr->InterruptConfiguration[Vector].csr_register = HW_IMODE_DISABLED; + + // + // No need to actually reach out to the specific I/O CBC, but + // if this is the last CPU to detach, turn off the + // I/O CBC entry which generates the specified interrupt. + // (this is cleaner from a hardware perspective). + // + + if (LastDetach) { + hwentry = (PHWINTRMAP)HardwarePtr; + + if (Vector >= LOWEST_DEVICE_TASKPRI && + Vector < CBUS2_BROADCAST_TASKPRI_LOW) + hwentry->csr_register = HW_MODE_DISABLED; + } +} + + + + +/*++ + +Routine Description: + + Remove reset from the specified processor, allowing him to boot, + beginning execution at the specified start address. + +Arguments: + + Processor - Supplies a logical processor number to boot + + StartAddress - Supplies a start address containing real-mode code + for the processor to execute. + +Return Value: + + None. + +--*/ +VOID +Cbus2BootCPU ( +IN ULONG Processor, +IN ULONG StartAddress +) +{ + PULONG ResetAddress; + + // + // For Cbus2, the only hardware dependency when booting + // additional processors is to put the real-mode starting + // CS/EIP in 0x467 (the warm reset vector), and clear reset + // on the specified CPU. The start address has already been + // set up, so here just remove reset. + // + + UNREFERENCED_PARAMETER( StartAddress ); + + ResetAddress = (PULONG)((PUCHAR)CbusCSR[Processor].csr + + CbusGlobal.smp_creset); + + *ResetAddress = CbusGlobal.smp_creset_val; +} + +/*++ + +Routine Description: + + Overlay the irql-to-vector mappings with the Cbus2 + vector maps. Initialize the broadcast IPI address and the + "Irql-To-Cbus2-Hardware-Address" translation table. + + Also read the EISA interrupt edge/level specifications for + later use when enabling various EISA interrupts. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +VOID +Cbus2InitializePlatform(VOID) +{ + ULONG Index; + LONG Irql; + ULONG LowerVector; + ULONG Vector; + ULONG i; + extern ULONG CbusVectorToIrql[MAXIMUM_IDTVECTOR + 1]; + + // + // pick up the the EISA interrupt edge/level requests + // + + Cbus2InitInterruptPolarity(); + + // + // overlay the irql-to-vector mappings with the Cbus2 layout + // + + RtlMoveMemory( (PVOID)CbusIrqlToVector, + (PVOID)Cbus2IrqlToVector, + (HIGH_LEVEL + 1) * sizeof (ULONG)); + + for (Index = 0; Index < HIGH_LEVEL + 1; Index++) { +#ifdef CBC_REV1 + Cbus2IrqlToCbus2Addr[Index] = + (Cbus2IrqlToVector[Index] << CBUS2_REGISTER_SHIFT) + + FIELD_OFFSET(CSR_T, InterruptRequest); +#else + Cbus2IrqlToCbus2Addr[Index] = + (ULONG)(Cbus2IdentityCSR->InterruptRequest) + + (Cbus2IrqlToVector[Index] << CBUS2_REGISTER_SHIFT); +#endif + } + + CbusRedirVector = CBUS2_REDIR_IPI; + CbusRebootVector = CBUS2_REBOOT_TASKPRI; + + // + // build the "vector-to-irql" mappings here for fast + // translation when accepting an interrupt. this is + // better than continually keeping fs:PcIrql updated, + // as it allows us to remove instructions from KfRaiseIrql + // and KfLowerIrql, the hot paths. Although this is done + // for each interrupt as it is individually enabled, this + // must also be done here for Cbus2 since multiple vectors + // can be grouped into a shared irql - this isn't done in + // Cbus1 because there isn't the concept of supporting multiple + // different kinds of I/O busses simultaneously. + // + for (Irql = HIGH_LEVEL; Irql > 0; Irql--) { + Vector = Cbus2IrqlToVector[Irql]; + LowerVector = Cbus2IrqlToVector[Irql - 1]; + for (i = Vector; i > LowerVector; i--) { + CbusVectorToIrql[i] = Irql; + } + } + + // + // The PCI bus number of the default C-bus II PCI bus bridge + // must be 0. At initialization time, the additional bus + // bridges should be set to 0xFF. they will be properly + // initialized in Cbus2InitiailizeOtherPCIBus(). + // + for (Index = 1; Index < Cbus2BridgesFound; Index++) { + Cbus2BridgePCIBusNumber[Index] = 0xFF; + } + Cbus2BridgePCIBusNumber[0] = 0; + + // + // Check if this is a PELE platform. + // If so, it uses a different interrupt routing + // algorithm for the 2nd bus. + // + Cbus2PeleSystemFound = Cbus2CheckForPeleSystem(); +} + +/*++ + +Routine Description: + + Initialize this processor's CSR, interrupts, spurious interrupts & IPI + vector, using the CBC (not the APIC) for interrupt control. + +Arguments: + + Processor - Supplies a logical processor number + +Return Value: + + None. + +--*/ +VOID +Cbus2InitializeCBC( +IN ULONG Processor +) +{ + PCSR csr; + PCSR Broadcast; + ULONG Index, ThisElement, Vector; + + ASSERT (Cbus2InterruptController == ELEMENT_HAS_CBC); + // + // Map this CPU's CSR stuff into his local + // address space for fast access. + // + csr = (PCSR)CbusCSR[Processor].csr; + + // + // save the interrupt address for this processor for + // streamlining the interrupt code + // + Cbus2SendIPI[Processor] = + (PULONG)&(csr->InterruptRequest[CBUS2_IPI_TASKPRI]); + + // + // map in the task priority register and start off at + // IRQL 0 - we are still protected by cli. + // +#ifdef CBC_REV1 + (PTASKPRI) KeGetPcr()->HalReserved[PCR_TASKPRI] = &csr->TaskPriority; + csr->TaskPriority.csr_register = 0; +#else + (PTASKPRI) KeGetPcr()->HalReserved[PCR_TASKPRI] = + &Cbus2IdentityCSR->TaskPriority; + Cbus2IdentityCSR->TaskPriority.csr_register = 0; +#endif + + // + // Create the spurious interrupt IDT entry for this processor + // + KiSetHandlerAddressToIDT(CBUS2_SPURIOUS_TASKPRI, HalpSpuriousInterrupt); + + // + // initialize the spurious vector for the CBC + // to generate when it detects inconsistencies. + // + csr->SpuriousVector.csr_register = CBUS2_SPURIOUS_TASKPRI; + + HalEnableSystemInterrupt(CBUS2_SPURIOUS_TASKPRI, HIGH_LEVEL, Latched); + + // + // generate NMIs (trap 2) when we get error interrupts. enabled + // faults already generate NMI traps by default. + // + csr->ErrorVector.LowDword = 2; + csr->InterruptControl.LowDword = CbusGlobal.InterruptControlMask; + csr->FaultControl.LowDword = CbusGlobal.FaultControlMask; + + ThisElement = Processor; + Broadcast = (PCSR)CbusBroadcastCSR; + + // + // This processor participates in all broadcast IPIs + // except for ones sent by this processor. + // + Vector = CBUS2_BROADCAST_TASKPRI_LOW; + + for (Index = 0; Index < MAX_ELEMENT_CSRS; Index++, Vector++) { + + if (Index == ThisElement) { + + // + // Map this element's broadcast interrupt entry + // to streamline the IPI sending code, because + // this is not done easily by the hardware. + // + + (ULONG)KeGetPcr()->HalReserved[PCR_BROADCAST] = + (ULONG)&Broadcast->InterruptRequest[Vector]; + if (Cbus2EnableBroadcast == 0) { + KiSetHandlerAddressToIDT(Vector,HalpIpiHandler); + HalEnableSystemInterrupt(Vector, + IPI_LEVEL, Latched); + } + } + else { + if (Cbus2EnableBroadcast) { + KiSetHandlerAddressToIDT(Vector,HalpIpiHandler); + HalEnableSystemInterrupt(Vector, + IPI_LEVEL, Latched); + } + } + } + + // + // Also initialize the directed IPI entry for this processor. + // + KiSetHandlerAddressToIDT(CBUS2_IPI_TASKPRI, HalpIpiHandler); + HalEnableSystemInterrupt(CBUS2_IPI_TASKPRI, IPI_LEVEL, Latched); + + // + // Create an interrupt gate so processors can + // be stopped cleanly prior to rebooting the system. + // + KiSetHandlerAddressToIDT(CBUS2_REBOOT_TASKPRI, + CbusRebootHandler); + HalEnableSystemInterrupt(CBUS2_REBOOT_TASKPRI, + IPI_LEVEL, Latched); +} + +/*++ + +Routine Description: + + Initialize all the I/O Apics in the system. This includes I/O APICs + on the EISA bridges, as well as any that may exist on native Cbus2 + I/O boards. + +Arguments: + + Processor - Supplies a logical processor number + +Return Value: + + None. + +--*/ +VOID +Cbus2InitializeApic( +IN ULONG Processor +) +{ + ULONG BridgeIndex; + ULONG WindowBaseAddress; + ULONG WindowBaseAddressShifted; + ULONG WindowOffsetAddress; + ULONG CsrPhysicalAddress; + WINDOWRELOC_T RegisterValue; + PCSR csr; + PCBUS2_ELEMENT Cbus2Element; + ULONG original_bridge; + ULONG BridgeId; + + for (BridgeIndex = 0; BridgeIndex < Cbus2BridgesFound; BridgeIndex++) { + // + // since each EISA bridge will have the I/O APIC at the + // same physical address, we must make each one unique so + // references are guaranteed to get to the desired one. + // this is done by mapping them into each bridge's window 0 + // relocation register, and thus onto the PCBusWindow0. + // + csr = (PCSR)Cbus2BridgeCSR[BridgeIndex]; + CsrPhysicalAddress = Cbus2BridgeCSRPaddr[BridgeIndex].LowPart; + BridgeId = Cbus2BridgeId[BridgeIndex]; + + // + // save the original value for restoral after our I/O. + // repoint our I/O references to the desired bus bridge number. + // + original_bridge = csr->BusBridgeSelection.csr_register; + csr->BusBridgeSelection.csr_register = + ((original_bridge & ~MAX_ELEMENT_CSRS) | BridgeId); + + // + // this field must be set to the high 9 bits of the desired + // address. + // + WindowBaseAddressShifted = ((ULONG)CBUS2_IO_APIC_LOCATION >> 23); + WindowBaseAddress = (WindowBaseAddressShifted << 23); + WindowOffsetAddress = (ULONG)CBUS2_IO_APIC_LOCATION-WindowBaseAddress; + + RegisterValue.csr_register = csr->BridgeWindow0.csr_register; + RegisterValue.ra.WindowBase = WindowBaseAddressShifted; + csr->BridgeWindow0.csr_register = RegisterValue.csr_register; + + Cbus2Element = (PCBUS2_ELEMENT) + (CsrPhysicalAddress - CBUS_CSR_OFFSET); + + CbusInitializeIOApic(Processor, + (PVOID)((ULONG)&Cbus2Element->PCBusWindow0 + WindowOffsetAddress), + CBUS2_REDIR_IPI, + CBUS2_REBOOT_TASKPRI, + Cbus2IrqPolarity[BridgeIndex]); + + // + // restore our default bridge references to what they were + // when we started... + // + csr->BusBridgeSelection.csr_register = original_bridge; + } + + // + // NOTE: + // add above for native Cbus2 cards using APICs based on cbdriver.c + // this will also involve changes to the CbusInitializeIOApic code. + // +} + +/*++ + +Routine Description: + + Initialize this processor's CSR, interrupts, spurious interrupts & IPI + vector. This routine is also responsible for setting the initial IRQL + value for this processor to 0 - this is so the first call to KfRaiseIrql + from the kernel will return a level 0 as the "previous level". note that + lowering the task priority to irql 0 is harmless at this point because + we are protected by cli. + +Arguments: + + Processor - Supplies a logical processor number + +Return Value: + + None. + +--*/ +VOID +Cbus2InitializeCPU( +IN ULONG Processor +) +{ + PCSR csr; + ULONG cbc_config; + + // + // Disable all of this processor's incoming interrupts _AND_ + // any generated by his local CBC (otherwise they could go to + // any processor). This has to be done regardless of whether + // APIC or CBC mode is being used. + // + Cbus2DisableMyInterrupts(Processor); + + // + // Setup the interrupt controller as specified by RRD - + // currently we support either CBC or APIC... + // + + if (Cbus2InterruptController == ELEMENT_HAS_CBC) { + Cbus2InitializeCBC(Processor); + } + + else if (Cbus2InterruptController == ELEMENT_HAS_APIC) { + CbusInitializeLocalApic(Processor, CBUS2_LOCAL_APIC_LOCATION, + CBUS2_SPURIOUS_TASKPRI); + + // + // Only the boot processor initializes all the I/O APICs + // on all the EISA bridges and native Cbus2 cards. + // + + if (Processor == 0) { + Cbus2InitializeApic(Processor); + } + else { + KiSetHandlerAddressToIDT(CBUS2_REBOOT_TASKPRI, + CbusRebootHandler); + HalEnableSystemInterrupt(CBUS2_REBOOT_TASKPRI, + IPI_LEVEL, Latched); + } + } + else { + FatalError(MSG_OBSOLETE_PIC); + } + + // + // Set up a pointer to the global system timer for all of the + // processors. We directly access this from our low-level + // assembly code. + // + // Since we are setting this in global C-bus address space + // by using the broadcast mechanism, all the processors + // can provide a uniform synchronized view via + // KeQueryPerformanceCounter. + // + + if (Processor == 0) { + KeInitializeSpinLock(&Cbus2NMILock); + + csr = (PCSR)CbusCSR[Processor].csr; + + Cbus2TimeStamp0 = &csr->SystemTimer.LowDword; +#ifdef CBC_REV1 + CbusTimeStamp = + &((PCSR)CbusBroadcastCSR)->SystemTimer.LowDword; +#else + CbusTimeStamp = + &((PCSR)Cbus2IdentityCSR)->SystemTimer.LowDword; +#endif + + if (CbusGlobal.Cbus2Features & CBUS_ENABLED_PW) { + +#if DBG + DbgPrint("Enabling posted writes\n"); +#endif + + // + // if the posted-writes bit is enabled, then + // allow EISA I/O cycles to use posted writes. + // + // call a function here so the compiler won't use byte + // enables here - we must force a dword access. + // + cbc_config = Cbus2ReadCSR(&csr->CbcConfiguration.LowDword); + Cbus2WriteCSR(&csr->CbcConfiguration.LowDword, + cbc_config & ~CBC_DISABLE_PW); + } + + // + // C-bus II hardware work-around that is causing less + // than optimal code in the CBUS_EOI macro. + // + if ((CbusGlobal.Cbus2Features & + CBUS_DISABLE_LEVEL_TRIGGERED_INT_FIX) == 0) { + Cbus2FixLevelInterrupts = 1; + } + + // + // C-bus II hardware work-around for spurious interrupts + // on IRQ0 when edge-triggered interrupts are EOI'd + // and the interrupt is high. work-around consists of + // detecting spurious clock interrupts in Cbus2ClockInterrupt() + // and Cbus2ClockInterruptPx(). + // + if ((CbusGlobal.Cbus2Features & + CBUS_DISABLE_SPURIOUS_CLOCK_CHECK) == 0) { + Cbus2CheckSpuriousClock = 1; + } + + // + // C-bus II hardware work-around that prevents Cbus2RequestIpi() + // from using the CSR broadcast space for sending IPIs. + // + if ((CbusGlobal.Cbus2Features & CBUS_ENABLE_BROADCAST) == 1) { + Cbus2EnableBroadcast = 1; + } + } +} + + +/*++ + +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. + + HalGetInterruptVector() must maintain a "vector to interrupt" + mapping so when the interrupt is enabled later via + HalEnableSystemInterrupt(), something intelligent can be done - + ie: which CBC's hardware interrupt maps to enable! + this applies both to EISA bridge CBCs and Cbus2 native I/O CBC's. + + Note that HalEnableSystemInterrupt() will be CALLED by + EACH processor wishing to participate in the interrupt receipt. + + Do not detect collisions here because interrupts are allowed to be + shared at a higher level - the ke\i386\intobj.c will take care of + the sharing. Just make sure that for any given irq line, only one + vector is generated, regardless of how many drivers may try to share + the line. + +Arguments: + + BusInterruptLevel - Supplies the bus specific interrupt level. + + BusInterruptVector - Supplies the bus specific interrupt vector. + + Irql - Returns the system request priority. + +Return Value: + + Returns the system interrupt vector corresponding to the specified device. + +--*/ +ULONG +Cbus2MapVector( +IN PBUS_HANDLER RootHandler, +IN ULONG BusInterruptLevel, +IN ULONG BusInterruptVector, +OUT PKIRQL Irql +) +{ + ULONG SystemVector; + ULONG Index; + + UNREFERENCED_PARAMETER( BusInterruptVector ); + + if (RootHandler->ConfigurationType == CbusConfiguration) { + // + // Each CBC interrupt line on each board + // gets a different interrupt vector, ie: irq3 on CBC 1 + // gets a different vector from irq3 on CBC2. + // + SystemVector = Cbus2CBCIrqlines[BusInterruptLevel].Vector + + Cbus2MapBusNumberToBridgeIndex(RootHandler->BusNumber); + + // + // Group each pair of CBC irqs into a single IRQL. + // + *Irql = Cbus2CBCIrqlines[BusInterruptLevel].Irql; + } + else { + + // + // Must be EISA, ISA, PCI or MCA... + // + // For Cbus2, CBUS_MAX_BRIDGES(==2) entries have been allocated per + // NT IRQL level, with each irq line getting its own _vector_. + // However, the same irq on each EISA bus shares the same NT IRQL + // level, since not enough IRQLs are provided to make them + // unique also. + // + + // + // note that EISA bus 0 gets the lower vector and EISA bus 1 gets + // the higher vector in each vector pair. this fact is relied + // upon by Cbus2EnableDeviceInterrupt(). + // + Index = Cbus2MapBusNumberToBridgeIndex(RootHandler->BusNumber); + SystemVector = Cbus2EISAIrqlines[Index][BusInterruptLevel].Vector; + + *Irql = Cbus2EISAIrqlines[Index][BusInterruptLevel].Irql; + } + + return SystemVector; +} + + +/*++ + +Routine Description: + + "Link" a given vector to the passed BusNumber/irqline, returning + a "handle" that can be used to reference it later for operations + that must access the hardware (ie: Enable & DisableInterrupt). + +Arguments: + + InterfaceType - Supplies the type of bus which the vector is for. + + Vector - Supplies the system interrupt vector corresponding to the + specified BusNumber/Irqline. + + BusNumber - Supplies the bus number for the device. + + Irqline - Supplies the IRQ line of the specified interrupt + +Return Value: + + A hardware-specific pointer (actually a CSR hardware interrupt map address) + that is interpreted only by the Cbus2 backend. + +--*/ +PVOID +Cbus2LinkVector( +IN PBUS_HANDLER RootHandler, +IN ULONG Vector, +IN ULONG Irqline +) +{ + PCSR csr; + PVOID Opaque; + extern PVOID CbusCBCtoCSR(ULONG); + + if (Cbus2InterruptController == ELEMENT_HAS_APIC) { + Opaque = CbusApicLinkVector(RootHandler, Vector, Irqline); + return Opaque; + } + + if (RootHandler && RootHandler->ConfigurationType == CbusConfiguration) { + // + // map the CBC hardware interrupt map on the Cbus2 native CBC + // that the hardware is attached to. note that this is + // purely a physical location issue; the access both to and from + // the driver's hardware is fully symmetric for ALL processors. + // + csr = (PCSR) CbusCBCtoCSR( + Cbus2MapBusNumberToBridgeIndex(RootHandler->BusNumber)); + } + else { + + // + // For any EISA interrupts, just point the caller at the + // corresponding bridge entry. + // + // Note also that this section of code is called by + // Cbus2SetupPrivateVectors() to set up CBC + // mappings at Phase0 for interrupts that must be enabled + // during Phase0. At that point in startup, the bus + // enumeration structures don't exist, so we default to + // EISA bridge 0 for any requests at that point in time. + // + + if (!RootHandler) + csr = (PCSR)Cbus2BridgeCSR[BRIDGE0]; + else + csr = (PCSR)Cbus2BridgeCSR[ + Cbus2MapBusNumberToBridgeIndex(RootHandler->BusNumber)]; + } + + return (PVOID)csr; +} + +// +// +// internal Cbus2 support routines begin here +// +// +// + +/*++ + +Routine Description: + + by default, disable all of the calling processor's + interrupt configuration registers(ICR) so he will take no interrupts. + also disable all interrupts originating from his CBC, so + no other processor will get interrupts from any devices + attached to this CBC. + + as each interrupt is enabled, it will need to be enabled + at this CBC, and also in each receiving processors' ICR. + + all EISA bridges have had their interrupts disabled already. + as each interrupt is enabled, it will need to be enabled + at the bridge, and also on each processor participating + in the reception. this needs to be done for both APIC and CBC + modes. + +Arguments: + + Processor - Supplies the caller's logical processor number whose + interrupts will be disabled + +Return Value: + + None. + +--*/ +VOID +Cbus2DisableMyInterrupts( +IN ULONG Processor +) +{ + ULONG Vector; + PCSR csr; + + csr = (PCSR)KeGetPcr()->HalReserved[PCR_CSR]; + + for (Vector = 0; Vector < INTR_CONFIG_ENTRIES; Vector++) { + csr->InterruptConfiguration[Vector].csr_register = + HW_IMODE_DISABLED; + } + + // + // In the midst of setting up the EISA element CBCs or + // processor CBCs (for those with native Cbus2 devices attached), + // a device interrupt that was pending in a bridge's + // 8259 ISRs may be lost. None should be fatal, even an + // 8042 keystroke, since the keyboard driver should do a flush + // on open, and thus recover in the same way the standard + // uniprocessor NT HAL does when it initializes 8259s. + // + // the hardware interrupt map entries for this processor's + // CBC are disabled by RRD prior to booting each processor, + // so it doesn't need to be done here. + // +} + +/*++ + +Routine Description: + + Allocate an interrupt vector for a Cbus2 native device. + +Arguments: + + BusHandler - Supplies the parent (ie: Internal) pointer + + RootHandler - Supplies the bus (ie: Cbus2) for the device. For Cbus2 + native I/O devices, the bus number is actually the CBC + number index in the CbusIoElements[] table. + + BusInterruptLevel - Supplies the IRQ line of the specified interrupt + + BusInterruptVector - Unused + + Irql - Returns the IRQL associated with this interrupt request + + Affinity - Returns the mask of processors participating in receipt + of this interrupt + +Return Value: + + Returns the system interrupt vector corresponding to the + specified BusNumber/BusInterruptLevel. + +--*/ +ULONG +Cbus2GetInterruptVector( +IN PBUS_HANDLER BusHandler, +IN PBUS_HANDLER RootHandler, +IN ULONG BusInterruptLevel, +IN ULONG BusInterruptVector, +OUT PKIRQL Irql, +OUT PKAFFINITY Affinity +) +{ + + extern ULONG CbusIoElementIndex; + + UNREFERENCED_PARAMETER( BusInterruptVector ); + +#if 0 + // + // can't do this because we don't include ntddk.h + // + ASSERT (RootHandler->InterfaceType == Cbus); +#endif + + if (BusInterruptLevel >= REV1_HWINTR_MAP_ENTRIES) { + + // + // Illegal BusInterruptVector - do not connect. + // + + return 0; + } + + // + // Get parent's translation from here... + // + return BusHandler->ParentHandler->GetInterruptVector ( + BusHandler->ParentHandler, + RootHandler, + BusInterruptLevel, + BusInterruptVector, + Irql, + Affinity + ); +} + +/*++ + +Routine Description: + + Convert a PCI interrupt pin to an interrupt line. + Note that this work has already been done in + Cbus2HierarchicalPciBusSearch(). + +Arguments: + + BusHandler - Supplies the parent (ie: Internal) pointer + + RootHandler - Supplies the bus for the device + + SlotNumber - Slot number of request + + PciData - PCI configuration data + +Return Value: + + None. + +--*/ +VOID +Cbus2PCIPin2CB2Line ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciData + ) +{ +} + +/*++ + +Routine Description: + + Convert a PCI interrupt line to an interrupt pin. + Note that this work has already been done in + Cbus2HierarchicalPciBusSearch(). + +Arguments: + + BusHandler - Supplies the parent (ie: Internal) pointer + + RootHandler - Supplies the bus for the device + + SlotNumber - Slot number of request + + PciNewData - New PCI configuration data + + PciOldData - Old PCI configuration data + +Return Value: + + None. + +--*/ +VOID +Cbus2PCICB2Line2Pin ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PCI_SLOT_NUMBER SlotNumber, + IN PPCI_COMMON_CONFIG PciNewData, + IN PPCI_COMMON_CONFIG PciOldData + ) +{ +} + +/*++ + +Routine Description: + + Returns an interrupt range for the given PciSlot. + +Arguments: + + BusHandler - Supplies the parent (ie: Internal) pointer + + RootHandler - Supplies the bus for the device + + PciSlot - Slot number of request + + Interrupt - Pointer to returned Interrupt structure + +Return Value: + + None. + +--*/ +NTSTATUS +Cbus2GetFixedPCICB2Line ( + 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; + } + + (*Interrupt)->Base = PciData->u.type0.InterruptLine; + (*Interrupt)->Limit = PciData->u.type0.InterruptLine; + return STATUS_SUCCESS; +} + +VOID +Cbus2InitOtherBuses(VOID) +{ + PBUS_HANDLER Bus; + ULONG i; + extern ULONG CBCIndex; + + // + // For each Cbus2 CBC present, add a handler + // + for (i=0; i < CBCIndex; i++) { + Bus = HalpAllocateBusHandler ( + CBus, // Interface type + CbusConfiguration, // Has this configuration space + i, // bus # + Internal, // child of this bus + 0, // and number + 0 // sizeof bus specific buffer + ); //(should be sizeof CBUS_IO_ELEMENTS_T + + // + // Add Cbus configuration space + // + + Bus->GetBusData = (PGETSETBUSDATA) Cbus2GetCbusData; + Bus->SetBusData = (PGETSETBUSDATA) Cbus2SetCbusData; + Bus->GetInterruptVector = (PGETINTERRUPTVECTOR) Cbus2GetInterruptVector; + +#if 0 + // + // NOT CODED YET: BusSpecific data is a pointer to the info + // (if possible this structure should be the bus specific info) + // + Bus->BusData = (PVOID) &CbusIoElements[i]; + Bus->AdjustResourceList + Bus->AssignSlotResources +#endif + Bus->AdjustResourceList = HalpNoAdjustResourceList; + Bus->AssignSlotResources = HalpNoAssignSlotResources; + + } +} + + +/*++ + +Routine Description: + + Check for a supported multiprocessor interrupt controller - currently + this means CBC or APIC only. + + Check for Cbus2 I/O bridges and disable their incoming interrupts + here. This cannot be done in HalInitializeProcessor() because generally + the I/O bridge will not have a CPU on it. + +Arguments: + + Table - Supplies a pointer to the RRD extended ID information table + + Count - Supplies a pointer to the number of valid entries in the + RRD extended ID information table + +Return Value: + + None. + +--*/ +VOID +Cbus2ParseRRD( +IN PEXT_ID_INFO Table, +IN OUT PULONG Count +) +{ + ULONG Index; + PEXT_ID_INFO Idp; + PCSR csr; + UCHAR control_register; + ULONG original_bridge; + PCBUS2_ELEMENT Cbus2Element; + WINDOWRELOC_T RegisterValue; + PCHAR BusMemoryWindow0; + PCHAR BusMemoryWindow1; + extern VOID CbusDisable8259s(USHORT); + + for (Idp = Table, Index = 0; Index < *Count; Index++, Idp++) { + + // + // map the identity range of the CSR space so each CPU + // can access his own CSR without knowing his slot id. + // this is useful because the task priority (and other + // registers) can now be referenced at the same physical + // address regardless of which processor you're currently + // executing on. if you couldn't reference it at the same + // physical address (regardless of processor), then you + // would need to pushfd/cli/popfd around capturing the + // current task priority address and setting it. otherwise + // you could be context switched in between, and you'd + // corrupt the initial processor's task priority!!! + // + if (Idp->id == CbusGlobal.broadcast_id) { + // + // unfortunately, the size and offset of the identity + // map is hardcoded in. we really should get RRD to + // tell us this offset. + // + Cbus2Element = (PCBUS2_ELEMENT) + (Idp->pel_start - CBUS_CSR_OFFSET); + + Cbus2IdentityCSR = HalpMapPhysicalMemoryWriteThrough ( + (PVOID)(Cbus2Element->IdentityMappedCsr), + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + (ULONG)Cbus2Element->IdentityMappedCsr, + Idp->pel_size)); + continue; + } + + if (Idp->id == LAST_EXT_ID) + break; + + // check only processor elements... + + if (Idp->pm == 0) + continue; + + // + // at least the base bridge must have a + // distributed interrupt chip (because + // any CPUs without them will be disabled). + // + if (Idp->id != CbusGlobal.bootid) + continue; + + // + // check for CBC first, since if types of interrupt + // controllers are marked present, we will default to + // the CBC. + // + if (Idp->pel_features & ELEMENT_HAS_CBC) { + Cbus2InterruptController = ELEMENT_HAS_CBC; + continue; + } + + if (Idp->pel_features & ELEMENT_HAS_APIC) { + Cbus2InterruptController = ELEMENT_HAS_APIC; + // + // patch the ipi vector to one compatible with + // the cbusapic.asm code. + // + Cbus2IrqlToVector[IPI_LEVEL] = CBUS2_ALTERNATE_IPI; + continue; + } + } + + // + // there must be at least an APIC or a CBC for us to use + // + if ((Cbus2InterruptController & + (ELEMENT_HAS_APIC | ELEMENT_HAS_CBC)) == 0) + FatalError(MSG_OBSOLETE_PIC); + + for (Idp = Table, Index = 0; Index < *Count; Index++, Idp++) { + + if ((Idp->pel_features & ELEMENT_BRIDGE) == 0) { + continue; + } + + csr = HalpMapPhysicalMemoryWriteThrough ( + (PVOID)Idp->pel_start, + (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES( + Idp->pel_start, Idp->pel_size)); + + // + // to go from 8259 to CBC (or APIC) mode for interrupt handling, + // + // a) disable PC compatible interrupts, ie: stop each + // bridge CBC from asking its 8259 to satisfy INTA + // pulses to the CPU. + // b) mask off ALL 8259 interrupt input lines EXCEPT + // for irq0. since clock interrupts are not external + // in the EISA chipset, the bridge 8259 must enable + // them even when the CBC is enabled. putting the + // 8259 in passthrough mode (ie: the 8259 irq0 input + // will just be wired straight through) WILL NOT + // allow the 8259 to actually talk to the CPU; it + // just allows the interrupt to be seen by the CBC. + // the CBC is responsible for all the CPU interrupt + // handshaking. + // c) enable each participating element's (ie: CPUs only) + // interrupt configuration register for the vector + // the HAL has programmed irq0 to actually generate. + // d) initialize the hardware interrupt map for the irq0 + // entry. + // + // IT IS CRITICAL THAT THE ABOVE STEPS HAPPEN IN THE + // ORDER OUTLINED, OTHERWISE YOU MAY SEE SPURIOUS + // INTERRUPTS. + // + + // + // now process this I/O bridge: + // + // currently assumes that all bridges will be of the same + // flavor. if this element is a bridge, map it systemwide + // and disable all incoming interrupts on this bridge. + // any extra bridges beyond our configuration maximum + // are just disabled, and not used by NT. + // + + if (Cbus2BridgesFound < CBUS_MAX_BRIDGES) { + Cbus2BridgeCSR[Cbus2BridgesFound] = csr; + Cbus2BridgeId[Cbus2BridgesFound] = Idp->id; + Cbus2BridgeCSRPaddr[Cbus2BridgesFound].HighPart = 0; + Cbus2BridgeCSRPaddr[Cbus2BridgesFound].LowPart = + Idp->pel_start; + if ((Idp->id != CBUS2_DEFAULT_BRIDGE_ID) && + (csr->IoTypeInformation.LowDword != CBUS2_ELEMENT_TYPE_RAS)) { + // + // Use PC Bus Window 0 and 1 of the SPP + // bus bridge for memory space allocation + // of PCI devices on the SPP bus. + // + Cbus2Element = (PCBUS2_ELEMENT)((PCHAR) Idp->pel_start - + CBUS_CSR_OFFSET); + if (Cbus2InterruptController == ELEMENT_HAS_CBC) { + BusMemoryWindow0 = Cbus2Element->PCBusWindow0; + RegisterValue.csr_register = + csr->BridgeWindow0.csr_register; + RegisterValue.ra.WindowBase = + (ULONG)BusMemoryWindow0 >> + CBUS2_WINDOW_REGISTER_SHIFT; + csr->BridgeWindow0.csr_register = + RegisterValue.csr_register; + } + BusMemoryWindow1 = Cbus2Element->PCBusWindow1; + RegisterValue.csr_register = + csr->BridgeWindow1.csr_register; + RegisterValue.ra.WindowBase = + (ULONG)BusMemoryWindow1 >> + CBUS2_WINDOW_REGISTER_SHIFT; + csr->BridgeWindow1.csr_register = + RegisterValue.csr_register; + + // + // Set Memory hole register of each additional + // bus bridge. + // + csr->PcBusMemoryHoles0.LowDword=CBUS2_NO_MEMORY_HOLES; + csr->PcBusMemoryHoles1.LowDword=CBUS2_NO_MEMORY_HOLES1; + csr->PcBusMemoryHoles2.LowDword=CBUS2_NO_MEMORY_HOLES2; + + // + // Turn LED of SPP bus bridge on. + // + csr->LED.LowDword = CBUS2_LED_ON; + } + if (csr->IoTypeInformation.LowDword!=CBUS2_ELEMENT_TYPE_RAS) + Cbus2BridgesFound++; + } + + if (Idp->pel_features & ELEMENT_HAS_8259) { + + // + // disable all inputs in the 8259 IMRs except for the + // irq0. and explicitly force these masks onto the + // 8259s. + // + // if profiling is disabled, we will disable it in + // the interrupt configuration registers, but still + // we must leave the 8259 irq0 enabled. not to worry, + // the processor will not see irq0 interrupts. + // this way, if profiling is re-enabled later, we + // only need to change the interrupt configuration + // registers, and bingo, we provide the desired effect. + // + + // + // save the original value for restoral after our read. + // repoint our I/O references to the desired bus + // bridge number. + // + original_bridge = csr->BusBridgeSelection.csr_register; + csr->BusBridgeSelection.csr_register = + ((original_bridge & ~MAX_ELEMENT_CSRS) | Idp->id); + + control_register = + READ_PORT_UCHAR((PUCHAR)CbusGlobal.Control8259Mode); + WRITE_PORT_UCHAR((PUCHAR)CbusGlobal.Control8259Mode, + (UCHAR)(control_register | (UCHAR)CbusGlobal.Control8259ModeValue)); + +#ifdef MCA + CbusDisable8259s(0xFFFF); +#else + CbusDisable8259s(0xFFFE); +#endif + + // + // restore our default bridge references to what they + // were when we started... + // + csr->BusBridgeSelection.csr_register = original_bridge; + } + + // + // In the midst of setting up the EISA element CBCs or + // processor CBCs (for those with Cbus2 native devices), a + // device interrupt that was pending in a bridge's 8259 ISRs + // may be lost. None should be fatal, even an + // 8042 keystroke, since the keyboard driver does a flush + // on open, and will, thus recover in the same way the standard + // uniprocessor NT HAL does when it initializes 8259s. + // + // If RRD instructed us to operate in APIC mode, then we want + // all the hardware interrupt map registers disabled as well. + // so this code works for both CBC and APIC modes. + // + // the hardware interrupt map entries for this processor's + // CBC are disabled by RRD prior to booting each processor, + // so it doesn't need to be done here. + // + } +} + +/*++ + +Routine Description: + + Called to put all the other processors in reset prior to reboot. + In order to safely put the other processors in reset, an IPI is + sent to the other processors to force them to write-invalidate + their L1 cache and halt. The calling processor will wait 5 seconds + for the IPI to take affect. + + Once accomplished, the calling processor then clears all hardware + interrupt maps on the default bridge. The interrupt controller + is reset back to the 8259s. The keyboard controller is then + reset and ThisProcessor spins awaiting the reboot. + + This routine can be called at any IRQL and can be called + whilst cli'd at interrupt time. + +Arguments: + + ThisProcessor - Supplies the caller's logical processor number + +Return Value: + + None. + +--*/ +VOID +Cbus2ResetAllOtherProcessors( +IN ULONG ThisProcessor +) +{ + PHWINTRMAP hwentry; + PCSR io_csr; + ULONG Index; + ULONG Irq; + PCSR csr; + UCHAR control_register; + + // + // repoint our I/O references to the default bus bridge + // don't bother saving and restoring the current bridge + // selection since we're about to reboot anyway. + // + csr = (PCSR)CbusCSR[ThisProcessor].csr; + csr->BusBridgeSelection.csr_register = Cbus2BridgeId[0]; + + control_register = READ_PORT_UCHAR((PUCHAR)CbusGlobal.Control8259Mode); + + // + // we need to protect ourselves from interrupts as the + // CBC will be disabled with our next write and the 8259s + // will be in control... + // + _asm { + cli + } + + // + // IPI the additional processors to get them to flush + // their internal caches and halt. this will be more + // conducive for the subsequent reset. + // + for (Index = 0; Index < CbusProcessors; Index++) { + if (Index == ThisProcessor) + continue; + csr = (PCSR)CbusCSR[Index].csr; + Cbus2WriteCSR((PULONG) + &(csr->InterruptRequest[CBUS2_REBOOT_TASKPRI]), 1); + } + + // + // Delay 5 seconds to give the additional processors + // a chance to get the previous IPI. + // + KeStallExecutionProcessor(5000000); + + // + // Disable all the bridge hardware interrupt maps. + // This will disable all EISA IRQ interrupts. + // + for (Index = 0; Index < Cbus2BridgesFound; Index++) { + io_csr = (PCSR)Cbus2BridgeCSR[Index]; + for (Irq = 0; Irq <= EISA_IRQLS; Irq++) { + hwentry = &io_csr->HardwareInterruptMap[Irq]; + hwentry->csr_register = 0; + } + } + + WRITE_PORT_UCHAR((PUCHAR)CbusGlobal.Control8259Mode, + (UCHAR)(control_register & ~(UCHAR)CbusGlobal.Control8259ModeValue)); + + // + // reset the keyboard controller. + // + WRITE_PORT_UCHAR(KEYBPORT, RESET); +loop: + goto loop; +} + +/*++ + +Routine Description: + + This function initializes the HAL-specific hardware device + (CLOCK & PROFILE) interrupts for the Corollary Cbus2 architecture. + +Arguments: + + none. + +Return Value: + + VOID + +--*/ +VOID +Cbus2InitializeDeviceIntrs( +IN ULONG Processor +) +{ + PCSR csr; + ULONG TimeStamp; + extern VOID Cbus2ClockInterrupt(VOID); + extern VOID Cbus2ClockInterruptPx(VOID); + + // + // here we initialize & enable all the device interrupts. + // this routine is called from HalInitSystem. + // + // each processor needs to call KiSetHandlerAddressToIDT() + // and HalEnableSystemInterrupt() for himself. + // + + if (Processor == 0) { + + // + // Support the HAL's exported interface to the rest of the + // system for the IDT configuration. This routine will + // also set up the IDT entry and enable the actual interrupt. + // + // Only one processor needs to do this, especially since + // the additional processors are vectoring elsewhere for speed. + // + + HalpEnableInterruptHandler ( + DeviceUsage, // Mark as device vector + IRQ0, // Bus interrupt level + CbusClockVector, // System IDT + CLOCK2_LEVEL, // System Irql + Cbus2ClockInterrupt, // ISR + Latched); + + HalpEnableInterruptHandler ( + DeviceUsage, // Mark as device vector + IRQ8, // Bus interrupt level + ProfileVector, // System IDT + PROFILE_LEVEL, // System Irql + HalpProfileInterrupt, // ISR + Latched); + + } + else { + // + // if the spurious clock check is enabled, then + // synchronize this CPU's SystemTimer by capturing CPU0's. + // + if (Cbus2CheckSpuriousClock) { + csr = (PCSR)CbusCSR[Processor].csr; + TimeStamp = Cbus2ReadCSR(Cbus2TimeStamp0); + Cbus2WriteCSR(&csr->SystemTimer.LowDword, TimeStamp); + } + + KiSetHandlerAddressToIDT(CbusClockVector,Cbus2ClockInterruptPx); + HalEnableSystemInterrupt(CbusClockVector, CLOCK2_LEVEL, Latched); + + KiSetHandlerAddressToIDT(ProfileVector, HalpProfileInterruptPx); + HalEnableSystemInterrupt(ProfileVector, PROFILE_LEVEL, Latched); + } +} + +/*++ + +Routine Description: + + Translate a Root BusNumber to a C-bus II bridge index. + +Arguments: + + Root BusNumber - Supplies the child bus. + +Return Value: + + Index - The C-bus II bridge index. + +--*/ +ULONG +Cbus2MapBusNumberToBridgeIndex( +ULONG RootBusNumber +) +{ + ULONG Index; + + for (Index = Cbus2BridgesFound - 1; Index >= 0; Index--) { + if (RootBusNumber >= Cbus2BridgePCIBusNumber[Index]) + break; + } + + return Index; +} + +/*++ + +Routine Description: + + Translate a bridge ID to an index. + +Arguments: + + BridgeId - Supplies the bridge ID. + +Return Value: + + Index - The C-bus II bridge index. + +--*/ +ULONG +Cbus2MapBridgeIdToIndex( +ULONG BridgeId +) +{ + ULONG Index; + + for (Index = Cbus2BridgesFound - 1; Index >= 0; Index--) { + if (BridgeId == Cbus2BridgeId[Index]) + break; + } + + return Index; +} + +/*++ + +Routine Description: + + Check the passed pRange list for qualifying ranges that + can be mapped into C-bus space. + +Arguments: + + pRange - Supplies the range list. + + Base - Supplies the base of the C-bus II remappable range. + + Limit - Supplies the limit of the C-bus II remappable range. + + SystemBase - Supplies the SystemBase for the translation. + +Return Value: + + Number of new ranges added. + +--*/ +ULONG +Cbus2CheckRange( +PSUPPORTED_RANGE pRange, +ULONG Base, +ULONG Limit, +PCHAR SystemBase +) +{ + ULONG NewRanges = 0; + + for (; pRange; pRange = pRange->Next) { + // + // Check if in the range by checking for stradlers and fully + // contained Base/Limit pairs. + // + if ((pRange->Base <= Base && pRange->Limit >= Base) || + (pRange->Base >= Base && pRange->Base <= Limit)) { + + // + // Check for a base stradler. + // + if (pRange->Base < Base) { + HalpAddRange ( + pRange, + pRange->SystemAddressSpace, + pRange->SystemBase, + Base, + pRange->Limit + ); + pRange->Limit = Base - 1; + pRange = pRange->Next; + // + // Should still be sorted, but bump the count. + // + NewRanges++; + } + + // + // Check for a limit stradler. + // + if (pRange->Limit > Limit) { + HalpAddRange ( + pRange, + pRange->SystemAddressSpace, + pRange->SystemBase, + Limit + 1, + pRange->Limit + ); + pRange->Limit = Limit; + // + // Should still be sorted, but bump the count. + // + NewRanges++; + } + + // + // Set SystemBase for the qualifying pRange. + // Make sure that SystemAddressSpace is set to 0. + // + pRange->SystemBase = (ULONG)SystemBase; + pRange->SystemAddressSpace = 0; + } + } + + return NewRanges; +} + +/*++ + +Routine Description: + + Check the passed pRange list for limits that + exceed that new limit. If so, lower the limit. + +Arguments: + + pRange - Supplies the range list. + + NewLimit - Supplies the new limit for the given range list. + +Return Value: + + None. + +--*/ +VOID +Cbus2LowerLimit( +PSUPPORTED_RANGE pRange, +ULONG NewLimit +) +{ + for (; pRange; pRange = pRange->Next) { + if (pRange->Limit > NewLimit) + pRange->Limit = NewLimit; + // + // Check if the extent is no longer valid. + // + if (pRange->Base > pRange->Limit) { + pRange->Base = 0; + pRange->Limit = 0; + } + } +} + +/*++ + +Routine Description: + + Set the range for this pRange to the passed base and limit. + +Arguments: + + pRange - Supplies the range list. + + NewBase - Supplies the new base for the given range list. + + NewLimit - Supplies the new limit for the given range list. + +Return Value: + + None. + +--*/ +VOID +Cbus2SetRange( +PSUPPORTED_RANGE pRange, +LONGLONG NewBase, +LONGLONG NewLimit +) +{ + pRange->Base = NewBase; + pRange->Limit = NewLimit; + pRange->Next = 0; +} + +/*++ + +Routine Description: + + Called once, from HalReportResourceUsage(), to check all the + Ranges in all the PBUS_HANDLERs. note that it is currently + assumed that HalpInitializePciBus() is the last in the chain to + add and configure buses. if this condition changes, then the + calling of this routine will need to be adjusted. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +VOID +Cbus2CheckBusRanges(VOID) +{ + PBUS_HANDLER Bus; + PSUPPORTED_RANGES BusAddresses; + ULONG InterfaceType; + ULONG BridgeIndex; + ULONG BusNumber; + + // + // Check all the buses (InterfaceType, BusNumber) pairs ... + // + for (InterfaceType=0; InterfaceType<MaximumInterfaceType; InterfaceType++) { + for (BusNumber = 0; ; BusNumber++) { + if ((Bus = HaliHandlerForBus (InterfaceType, BusNumber)) == NULL) { + break; + } + if (Bus->InterfaceType != PCIBus) + break; + BusAddresses = Bus->BusAddresses; + if (BusAddresses) { + // + // Get the root bus, or C-bus II bridge index. + // The SystemBase will be either: + // + // IO - offset 0x3c10000 into the CSR space + // Memory - offset 0x0 into the CSR space + // + BridgeIndex = Cbus2MapBusNumberToBridgeIndex(Bus->BusNumber); + + // + // For compatibility with non-Microsoft maintained + // device drivers, don't translate bus 0 addresses. + // + if (BridgeIndex == 0) { + // + // ensure that the limit of any memory range + // on the primary bus bridge is below the + // CSR space. + // + Cbus2LowerLimit(&BusAddresses->PrefetchMemory, + CbusGlobal.cbusio - 1); + Cbus2LowerLimit(&BusAddresses->Memory, + CbusGlobal.cbusio - 1); + continue; + } + } + } + } +} + + +/*++ + +Routine Description: + + Called once, from HalReportResourceUsage(), to check all the + Ranges in all the PBUS_HANDLERs. note that it is currently + assumed that HalpInitializePciBus() is the last in the chain to + add and configure buses. if this condition changes, then the + calling of this routine will need to be adjusted. + +Arguments: + + Bus - Bus Handler to check. + + BridgeIndex - Logical bridge index. + +Return Value: + + None. + +--*/ +VOID +Cbus2AdjustSPPBusRange( +PBUS_HANDLER Bus, +UCHAR BridgeIndex, +UCHAR RootPciBus +) +{ + PSUPPORTED_RANGES BusAddresses; + PHYSICAL_ADDRESS CsrPhysicalAddress; + PCBUS2_ELEMENT Cbus2Element; + PCHAR IOSystemBase; + LONGLONG MemorySystemBase; + ULONG MemorySystemLimit; + + if (!(BusAddresses = Bus->BusAddresses)) + return; + // + // check if the range of the IO bus address is within + // CBUS2_IO_BASE_ADDRESS & CBUS2_IO_LIMIT. if so, + // add IOSystemBase to uniquely target the I/O to the + // SPP bus bridge. + // + CsrPhysicalAddress = Cbus2BridgeCSRPaddr[BridgeIndex]; + Cbus2Element = (PCBUS2_ELEMENT) + ((PCHAR)CsrPhysicalAddress.LowPart - CBUS_CSR_OFFSET); + IOSystemBase = Cbus2Element->PCBusIO; + BusAddresses->NoIO += Cbus2CheckRange(&BusAddresses->IO, + CBUS2_IO_BASE_ADDRESS, CBUS2_IO_LIMIT, IOSystemBase); + if (RootPciBus) { + if (Cbus2InterruptController == ELEMENT_HAS_CBC) { + MemorySystemBase = (ULONGLONG) + ((ULONG)Cbus2Element->PCBusWindow0); + MemorySystemLimit = ((ULONG)Cbus2Element->PCBusWindow0 + + (sizeof (Cbus2Element->PCBusWindow0) * 2) - 1); + } + else { + MemorySystemBase = (ULONGLONG) + ((ULONG)Cbus2Element->PCBusWindow1); + MemorySystemLimit = ((ULONG)Cbus2Element->PCBusWindow1 + + sizeof (Cbus2Element->PCBusWindow1) - 1); + } + Cbus2SetRange(&BusAddresses->PrefetchMemory, + MemorySystemBase, MemorySystemLimit); + Cbus2SetRange(&BusAddresses->Memory, + MemorySystemBase, MemorySystemLimit); + } +} + +/*++ + +Routine Description: + + Called once, from CbusEstablishMaps(), to find the holes in the + C-bus memory space that is not useable for device allocation. + These ranges are entered into a table and this table is then used: + + 1. to remove the hole ranges from the memory descriptor table as + passed in from the BIOS E820 function (see comment in HalpAddMem()). + + 2. to add to the resource list used by the C-bus HAL. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +VOID +Cbus2AddMemoryHoles(VOID) +{ + PCSR csr; + ULONG Index; + ULONG Start; + ULONG TLM; + + // + // HalpMemory[] lists all the valid memory ranges reported by RRD. + // use AddMemoryHole() and AddMemoryResource() to remove gaps + // between these memory ranges. + // + for (Index = 0; Index < HalpMemoryIndex - 1; Index++) { + + Start = PAGES_TO_BYTES(HalpMemory[Index].BasePage + + HalpMemory[Index].PageCount), + AddMemoryHole(Start, + PAGES_TO_BYTES(HalpMemory[Index+1].BasePage) - Start); + AddMemoryResource(Start, + PAGES_TO_BYTES(HalpMemory[Index+1].BasePage) - Start); + + } + + // + // the final memory gap is from the end of the last memory range + // through the TLM register. the TLM register marks the start + // of resource allocatable memory. the TLM register is in units + // of 256MB where 0 indicates 256MB. + // + csr = (PCSR)Cbus2BridgeCSR[BRIDGE0]; + TLM = (Cbus2ReadCSR(&csr->TLM.LowDword) + 1) * 256 * 1024 * 1024; + Start = PAGES_TO_BYTES(HalpMemory[Index].BasePage + + HalpMemory[Index].PageCount); + if (TLM - Start) { + AddMemoryHole(Start, TLM - Start); + AddMemoryResource(Start, TLM - Start); + } +} + +// +// Mask for valid bits of edge/level control register (ELCR) in 82357 ISP: +// ie: ensure irqlines 0, 1, 2, 8 and 13 are always marked edge, as the +// I/O register will not have them set correctly. All other bits in the +// I/O register will be valid without us having to poke them. +// +// If this is a microchannel machine, then we don't use the mask. +// +#define ELCR_MASK 0xDEF8 + +#define PIC1_ELCR_PORT (PUCHAR)0x4D0 // ISP edge/level control regs +#define PIC2_ELCR_PORT (PUCHAR)0x4D1 + +/*++ + +Routine Description: + + Called once to read the EISA interrupt configuration registers. + This will tell us which interrupt lines are level-triggered and + which are edge-triggered. Note that irqlines 0, 1, 2, 8 and 13 + are not valid in the 4D0/4D1 registers and are defaulted to edge. + +Arguments: + + None. + +Return Value: + + The interrupt line polarity of all the EISA irqlines on all the EISA + buses in the system. + +--*/ +VOID +Cbus2InitInterruptPolarity( +VOID +) +{ + ULONG InterruptLines; + ULONG BridgeIndex; + ULONG original_bridge; + ULONG BridgeId; + PCSR csr; + + for (BridgeIndex = 0; BridgeIndex < Cbus2BridgesFound; BridgeIndex++) { + InterruptLines = 0; + + // + // Read the edge-level control register (ELCR) so we'll know how + // to mark each driver's interrupt line (ie: edge or level + // triggered) in the CBC or APIC I/O unit redirection table + // entry. + // + + BridgeId = Cbus2BridgeId[BridgeIndex]; + csr = Cbus2BridgeCSR[BridgeIndex]; + + // + // save the original value for restoral after our read. + // repoint our I/O references to the desired bus bridge number. + // + original_bridge = csr->BusBridgeSelection.csr_register; + csr->BusBridgeSelection.csr_register = + ((original_bridge & ~MAX_ELEMENT_CSRS) | BridgeId); + + // + // read the ELCR register from the correct bus bridge + // + InterruptLines = ( ((ULONG)READ_PORT_UCHAR(PIC2_ELCR_PORT) << 8) | + ((ULONG)READ_PORT_UCHAR(PIC1_ELCR_PORT)) ); + + // + // restore our default bridge references to what they were + // when we started... + // + csr->BusBridgeSelection.csr_register = original_bridge; + + // + // Explicitly mark irqlines 0, 1, 2, 8 and 13 as edge, + // unless the system is a microchannel system. + // Leave all other irqlines at their current register values. + // + +#ifndef MCA + InterruptLines &= ELCR_MASK; +#endif + + Cbus2IrqPolarity[BridgeIndex] = InterruptLines; + } +} + +/*++ + +Routine Description: + + Call HalpPCISynchronizeType1() from ixpcibus.c for proper + configuration space synchronization and then adjust the + bridge selection csr to point to the desired bus. + +Arguments: + + BusHandler - BusHandler for PCI configuration read/write + + Slot - Desired slot. + + Irql - Pointer to hold previous Irql. + + PciCfg1 - Type 1 configuration access. + +Return Value: + + None. + +--*/ +VOID +Cbus2PCISynchronize( + IN PBUS_HANDLER BusHandler, + IN PCI_SLOT_NUMBER Slot, + IN PKIRQL Irql, + IN PPCI_TYPE1_CFG_BITS PciCfg1 +) +{ + PCSR csr = Cbus2IdentityCSR; + ULONG BusNumber; + ULONG BridgeId; + + HalpPCISynchronizeType1 (BusHandler, Slot, Irql, PciCfg1); + BusNumber = BusHandler->BusNumber; + if (Cbus2BridgeOverride) { + BusNumber = Cbus2BridgeOverride; + Cbus2BridgeOverride = 0; + } + BridgeId = Cbus2BridgeId[Cbus2MapBusNumberToBridgeIndex(BusNumber)]; + csr->BusBridgeSelection.csr_register = BridgeId; +} + +/*++ + +Routine Description: + + Restore the bridge selection csr to bus bridge 0 and + then call HalpPCIReleaseSynchronzationType1() in ixpcibus.c + to release the PCI configuration synchronization. + +Arguments: + + BusHandler - BusHandler for PCI configuration read/write + + Irql - Pointer that holds Irql to restore to. + +Return Value: + + None. + +--*/ +VOID +Cbus2PCIReleaseSynchronization( + IN PBUS_HANDLER BusHandler, + IN KIRQL Irql +) +{ + PCSR csr = Cbus2IdentityCSR; + + csr->BusBridgeSelection.csr_register = 0; + HalpPCIReleaseSynchronzationType1 (BusHandler, Irql); +} + +/*++ + +Routine Description: + + Read the PCI configuration for the designated PPB. + Update the bus fields for this bridge device. + +Arguments: + + BusNumber - Bus Number of Bridge device. + + Device - Device Number of Bridge device. + + Function - Function of Bridge device. + + PrimaryBus - set Primary bus to this value. + + SecondaryBus - set Secondary bus to this value. + + SubordinateBus - set Subordinate bus to this value. + +Return Value: + + None. + +--*/ +VOID +Cbus2SetPPBBridgeBuses( +ULONG BusNumber, +PCI_SLOT_NUMBER Slot, +ULONG Function, +ULONG PrimaryBus, +ULONG SecondaryBus, +ULONG SubordinateBus +) +{ + PBUS_HANDLER BusHandler; + PCI_SLOT_NUMBER SlotNumber; + PPCI_COMMON_CONFIG PciData; + UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; + + PciData = (PPCI_COMMON_CONFIG) iBuffer; + BusHandler = HalpHandlerForBus (PCIBus, BusNumber); + SlotNumber.u.bits.DeviceNumber = Slot.u.bits.DeviceNumber; + SlotNumber.u.bits.FunctionNumber = Function; + SlotNumber.u.bits.Reserved = 0; + HalpReadPCIConfig (BusHandler, SlotNumber, PciData, + 0, PCI_COMMON_HDR_LENGTH); + PciData->u.type1.PrimaryBus = (UCHAR)PrimaryBus; + PciData->u.type1.SecondaryBus = (UCHAR)SecondaryBus; + PciData->u.type1.SubordinateBus = (UCHAR)SubordinateBus; + HalpWritePCIConfig (BusHandler, SlotNumber, PciData, + 0, PCI_COMMON_HDR_LENGTH); +} + +/*++ + +Routine Description: + + Read the PCI configuration for the designated host bridge. + Update the bus fields for this bridge device. + +Arguments: + + BusNumber - Bus Number of Bridge device. + + Device - Device Number of Bridge device. + + Function - Function of Bridge device. + + PrimaryBus - set Primary bus to this value. + + SubordinateBus - set Subordinate bus to this value. + +Return Value: + + None. + +--*/ +VOID +Cbus2SetHostBridgeBuses( +ULONG BusNumber, +ULONG Device, +ULONG Function, +ULONG PrimaryBus, +ULONG SubordinateBus +) +{ + PBUS_HANDLER BusHandler; + PCI_SLOT_NUMBER SlotNumber; + PCBUS2_HOST_BRIDGE_CONFIG PciData; + UCHAR iBuffer[sizeof (CBUS2_HOST_BRIDGE_CONFIG)]; + ULONG BusOverride = 0; + static ULONG FirstTry = 1; + + PciData = (PCBUS2_HOST_BRIDGE_CONFIG) iBuffer; + BusHandler = HalpHandlerForBus (PCIBus, BusNumber); + SlotNumber.u.bits.DeviceNumber = Device; + SlotNumber.u.bits.FunctionNumber = Function; + SlotNumber.u.bits.Reserved = 0; +retry: + HalpReadPCIConfig (BusHandler, SlotNumber, PciData, + 0, sizeof (CBUS2_HOST_BRIDGE_CONFIG)); + if (PciData->VendorID == PCI_INVALID_VENDORID && FirstTry) { + FirstTry = 0; + BusHandler->BusNumber = 0; + Cbus2BridgeOverride = BusNumber; + BusOverride = BusNumber; + goto retry; + } + PciData->BusNumber = (UCHAR)PrimaryBus; + PciData->SubordinateBusNumber = (UCHAR)SubordinateBus; + Cbus2BridgeOverride = BusOverride; + HalpWritePCIConfig (BusHandler, SlotNumber, PciData, + 0, sizeof (CBUS2_HOST_BRIDGE_CONFIG)); + BusHandler->BusNumber = BusNumber; +} + +UCHAR Cbus2SPPInterruptRoute[] = { + 1, 4, 3, 2, + 2, 1, 4, 3, + 3, 2, 1, 4, + 4, 3, 2, 1 +}; + +UCHAR Cbus2SPPInterruptRouteForPele[] = { + 10, 10, 10, 10, + 11, 11, 11, 11, + 12, 12, 12, 12, + 13, 13, 13, 13 +}; + +/*++ + +Routine Description: + + Route the interrupt for the given device and interrupt pin + for the secondary peer pci bridge. + +Arguments: + + Device - Device number for the device to route. + + InterruptPin - Interrupt Pin requested + +Return Value: + + Resulting routed interrupt line. + +--*/ +UCHAR +Cbus2RouteSPPInterrupt( +ULONG Device, +UCHAR InterruptPin +) +{ + if (Device == 0 || InterruptPin == 0) { + return 0; + } + if (Cbus2PeleSystemFound) { + return Cbus2SPPInterruptRouteForPele[ + ((InterruptPin - 1) * 4) + ((Device - 1) % 4)]; + } + else { + return Cbus2SPPInterruptRoute[ + ((InterruptPin - 1) * 4) + ((Device - 1) % 4)]; + } +} + +UCHAR Cbus2PPBInterruptRoute[] = { + 1, 2, 3, 4, + 2, 3, 4, 1, + 3, 4, 1, 2, + 4, 1, 2, 3, +}; + +/*++ + +Routine Description: + + Route the interrupt for the given device and interrupt pin + for a PCI-to-PCI bridge. + +Arguments: + + BusNumber - Bus number for this device. + + Device - Device number for the device to route. + + InterruptPin - Current Interrupt Pin request. + +Return Value: + + Resulting routed interrupt line. + +--*/ +ULONG +Cbus2RoutePPBInterrupt( +ULONG BusNumber, +ULONG Device, +UCHAR InterruptPin +) +{ + PBUS_HANDLER Bus; + PPCIPBUSDATA BusData; + + while (TRUE) { + Bus = HaliHandlerForBus(PCIBus, BusNumber); + if (Bus->ParentHandler->InterfaceType == Internal || + Device == 0 || InterruptPin == 0) { + break; + } + InterruptPin = Cbus2PPBInterruptRoute[ + ((Device % 4) * 4) + (InterruptPin - 1)]; + BusNumber = Bus->ParentHandler->BusNumber; + BusData = (PPCIPBUSDATA) Bus->BusData; + Device = BusData->CommonData.ParentSlot.u.bits.DeviceNumber; + } + + return (Device << 8) | InterruptPin; +} + +/*++ + +Routine Description: + + Add an additional PCI bus to the registry. + +Arguments: + + PCIRegInfo - Global registry information. + +Return Value: + + None. + +--*/ +VOID +Cbus2AddPciBusToRegistry( +PPCI_REGISTRY_INFO PCIRegInfo +) +{ + UNICODE_STRING unicodeString; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE hBus; + NTSTATUS status; + UCHAR buf2[150]; + PCONFIGURATION_COMPONENT Component; + PCM_FULL_RESOURCE_DESCRIPTOR Description; + ACCESS_MASK DesiredAccess; + PBUS_HANDLER Bus; + ULONG mnum; + ULONG d; + + mnum = 0; + // + // Add another PCI bus in the registry. + // + for (; ;) { + // + // Find next available MultiFunctionAdapter key. + // + DesiredAccess = KEY_READ | KEY_WRITE; + swprintf ((PWCHAR) buf2, L"%s\\%d", + rgzMultiFunctionAdapter, mnum); + RtlInitUnicodeString (&unicodeString, (PWCHAR) buf2); + InitializeObjectAttributes( &objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + NULL, + (PSECURITY_DESCRIPTOR) NULL); + status = ZwOpenKey( &hBus, DesiredAccess, + &objectAttributes); + if (!NT_SUCCESS(status)) { + break; + } + // already exists, next + ZwClose (hBus); + mnum += 1; + } + ZwCreateKey (&hBus, + DesiredAccess, + &objectAttributes, + 0, + NULL, + REG_OPTION_VOLATILE, + &d + ); + // + // Add needed registry values for this + // MultifunctionAdapter entry. + // + RtlInitUnicodeString (&unicodeString, rgzIdentifier); + ZwSetValueKey (hBus, + &unicodeString, + 0L, + REG_SZ, + L"PCI", + sizeof (L"PCI") + ); + + RtlInitUnicodeString (&unicodeString, rgzConfigurationData); + Description = (PCM_FULL_RESOURCE_DESCRIPTOR) buf2; + Description->InterfaceType = PCIBus; + Description->BusNumber = (ULONG)PCIRegInfo->NoBuses; + Description->PartialResourceList.Version = 0; + Description->PartialResourceList.Revision = 0; + Description->PartialResourceList.Count = 0; + ZwSetValueKey (hBus, + &unicodeString, + 0L, + REG_FULL_RESOURCE_DESCRIPTOR, + Description, + sizeof (*Description) + ); + + RtlInitUnicodeString (&unicodeString, L"Component Information"); + Component = (PCONFIGURATION_COMPONENT) buf2; + RtlZeroMemory (Component, sizeof (*Component)); + + // + // Setting the Component structure values using the + // structure doesn't match the PCI bus 0 setting. + // Set the values by hand to match what ntloader did + // for bus 0. Proper code would be: + // + // Component->AffinityMask = 0xffffffff; + // + buf2[12] = 0xff; + buf2[13] = 0xff; + buf2[14] = 0xff; + buf2[15] = 0xff; + ZwSetValueKey (hBus, + &unicodeString, + 0L, + REG_BINARY, + Component, + 16 + ); + ZwClose (hBus); +} + +/*++ + +Routine Description: + + Search the given PCI bus for device and PPB devices. + As devices are found, route any needed interrupts pins. + As PPB devices are found, recursively descend to the lower bus. + +Arguments: + + BridgeIndex - Logical index of C-bus II bridge. + + PCIRegInfo - Global registry information. + + BusNumber - Bus Number to begin search from. + +Return Value: + + None. + +--*/ +VOID +Cbus2HierarchicalPciBusSearch( +UCHAR BridgeIndex, +PPCI_REGISTRY_INFO PCIRegInfo, +ULONG BusNumber +) +{ + PBUS_HANDLER ParentBus; + PBUS_HANDLER ChildBus; + PPCIPBUSDATA ChildBusData; + CONFIGBRIDGE CB; + PSUPPORTED_RANGES ParentBusAddresses; + PSUPPORTED_RANGES ChildBusAddresses; + LONGLONG IOLimit; + LONGLONG MemoryLimit; + LONGLONG PrefetchMemoryLimit; + ULONG Device; + ULONG Function; + ULONG SecondaryBus; + UCHAR InterruptPin; + ULONG Retval; + UCHAR Rescan; + BOOLEAN FoundDisabledBridge; + + CB.PciData = (PPCI_COMMON_CONFIG) CB.Buffer; + CB.SlotNumber.u.bits.Reserved = 0; + + CB.BusHandler = HalpHandlerForBus (PCIBus, BusNumber); + CB.BusData = (PPCIPBUSDATA) CB.BusHandler->BusData; + + // + // Set resource limits for PCI-to-PCI bridges found. + // Each is allowed 25% of the amount available on the parent. + // + ParentBus = CB.BusHandler; + ParentBusAddresses = ParentBus->BusAddresses; + IOLimit = ((ParentBusAddresses->IO.Limit - + ParentBusAddresses->IO.Base) + 1) >> 2; + MemoryLimit = ((ParentBusAddresses->Memory.Limit - + ParentBusAddresses->Memory.Base) + 1) >> 2; + PrefetchMemoryLimit = ((ParentBusAddresses->PrefetchMemory.Limit - + ParentBusAddresses->PrefetchMemory.Base) + 1) >> 2; + + // + // Seach all the devices of this bus. + // + for (Device = 0; Device < PCI_MAX_DEVICES; Device++) { + CB.SlotNumber.u.bits.DeviceNumber = Device; + + for (Function = 0; Function < PCI_MAX_FUNCTION; Function++) { + CB.SlotNumber.u.bits.FunctionNumber = Function; + + // + // 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)) { + Retval = Cbus2RoutePPBInterrupt(BusNumber, Device, + CB.PciData->u.type0.InterruptPin); + InterruptPin = (UCHAR)(Retval & 0xff); + Retval >>= 8; + CB.PciData->u.type0.InterruptLine = + Cbus2RouteSPPInterrupt(Retval, InterruptPin); + CB.PciData->LatencyTimer = CBUS2_PCI_LATENCY_TIMER; + HalpWritePCIConfig (CB.BusHandler, CB.SlotNumber, + CB.PciData, 0, PCI_COMMON_HDR_LENGTH); + continue; + } + + CB.PciData->Command |= + PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE | + PCI_ENABLE_IO_SPACE | PCI_ENABLE_SERR; + + // + // Found a PCI-PCI bridge. + // Set up its parent child relationships. + // + ChildBus = HalpAllocateAndInitPciBusHandler ( + PCIRegInfo->HardwareMechanism & 0xf, + PCIRegInfo->NoBuses, FALSE); + + PCIConfigHandler.Synchronize = Cbus2PCISynchronize; + PCIConfigHandler.ReleaseSynchronzation = + Cbus2PCIReleaseSynchronization; + + ChildBusData = (PPCIPBUSDATA) ChildBus->BusData; + ChildBus->GetInterruptVector = + (PGETINTERRUPTVECTOR) Cbus2GetInterruptVector; + ChildBusData->CommonData.Pin2Line = + (PciPin2Line) Cbus2PCIPin2CB2Line; + ChildBusData->CommonData.Line2Pin = + (PciLine2Pin) Cbus2PCICB2Line2Pin; + ChildBusData->GetIrqRange = Cbus2GetFixedPCICB2Line; + + // + // Assign I/O, Memory and Prefetch resources + // for this PPB. + // + // First reduce the parent bus ranges. + // + Cbus2SetRange(&ParentBusAddresses->IO, + ParentBusAddresses->IO.Base, + ParentBusAddresses->IO.Limit - IOLimit); + Cbus2SetRange(&ParentBusAddresses->Memory, + ParentBusAddresses->Memory.Base, + ParentBusAddresses->Memory.Limit - MemoryLimit); + Cbus2SetRange(&ParentBusAddresses->PrefetchMemory, + ParentBusAddresses->PrefetchMemory.Base, + ParentBusAddresses->PrefetchMemory.Limit - + PrefetchMemoryLimit); + // + // Next, set the ranges in the config space of the PPB. + // + CB.PciData->u.type1.IOBase = PCI_IO_TO_CFG( + ParentBusAddresses->IO.Limit + 1); + CB.PciData->u.type1.IOLimit = PCI_IO_TO_CFG( + ParentBusAddresses->IO.Limit + IOLimit); + CB.PciData->u.type1.MemoryBase = PCI_MEMORY_TO_CFG( + ParentBusAddresses->Memory.Limit + 1); + CB.PciData->u.type1.MemoryLimit = PCI_MEMORY_TO_CFG( + ParentBusAddresses->Memory.Limit + MemoryLimit); + CB.PciData->u.type1.PrefetchBase = PCI_PREFETCH_TO_CFG( + ParentBusAddresses->PrefetchMemory.Limit + 1); + CB.PciData->u.type1.PrefetchLimit = PCI_PREFETCH_TO_CFG( + ParentBusAddresses->PrefetchMemory.Limit + + PrefetchMemoryLimit); + // + // Lastly, set the ranges in the child bus handler. + // Some of this code tracks HalpGetPciBridgeConfig(). + // + ChildBusAddresses = ChildBus->BusAddresses; + ChildBusData->BridgeConfigRead = TRUE; + HalpSetBusHandlerParent (ChildBus, ParentBus); + ChildBusData->ParentBus = (UCHAR) ParentBus->BusNumber; + ChildBusData->CommonData.ParentSlot = CB.SlotNumber; + Cbus2SetRange(&ChildBusAddresses->IO, + ParentBusAddresses->IO.Limit + 1, + ParentBusAddresses->IO.Limit + IOLimit); + Cbus2SetRange(&ChildBusAddresses->Memory, + ParentBusAddresses->Memory.Limit + 1, + ParentBusAddresses->Memory.Limit + MemoryLimit); + Cbus2SetRange(&ChildBusAddresses->PrefetchMemory, + ParentBusAddresses->PrefetchMemory.Limit + 1, + ParentBusAddresses->PrefetchMemory.Limit + + PrefetchMemoryLimit); + + // + // Update the configuration space. + // + HalpWritePCIConfig (CB.BusHandler, CB.SlotNumber, + CB.PciData, 0, PCI_COMMON_HDR_LENGTH); + + // + // Adjust appropriate ranges. + // + Cbus2AdjustSPPBusRange(ChildBus, BridgeIndex, 0); + + // + // Set the bus numbers for this bridge. + // + Cbus2SetPPBBridgeBuses((UCHAR)BusNumber, CB.SlotNumber, 0, + (UCHAR)BusNumber, (UCHAR)PCIRegInfo->NoBuses, 0xff); + // + // Add an additional PCI bus to the registry. + // + Cbus2AddPciBusToRegistry(PCIRegInfo); + SecondaryBus = PCIRegInfo->NoBuses; + PCIRegInfo->NoBuses++; + Cbus2HierarchicalPciBusSearch(BridgeIndex, PCIRegInfo, + SecondaryBus); + Cbus2SetPPBBridgeBuses((UCHAR)BusNumber, CB.SlotNumber, 0, + (UCHAR)BusNumber, (UCHAR)SecondaryBus, + (UCHAR)(PCIRegInfo->NoBuses - 1)); + } + } +} + +/*++ + +Routine Description: + + If an additional C-bus bridge is present in the system, + perform a hierarchical scan of the bus, set up the interrupt + routing for all the devices on the bus and allocate and initialize + the bus handlers for the buses. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +VOID +Cbus2InitializeOtherPciBus( +VOID +) +{ + 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]; + volatile PPCI_REGISTRY_INFO PCIRegInfo; + PKEY_VALUE_FULL_INFORMATION ValueInfo; + PCM_FULL_RESOURCE_DESCRIPTOR Desc; + PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc; + PCONFIGURATION_COMPONENT Component; + PBUS_HANDLER Bus; + PPCIPBUSDATA BusData; + UCHAR BridgeIndex; + ULONG i, junk; + + // + // 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 each additional C-bus II PCI bus bridge + // + for (BridgeIndex = 1; BridgeIndex < Cbus2BridgesFound; BridgeIndex++) { + // + // Record the PCI Bus number for additional + // C-bus II PCI bus bridges. + // + Cbus2BridgePCIBusNumber[BridgeIndex] = + (ULONG)PCIRegInfo->NoBuses; + + // + // Initialize IRQ Polarity table for additional + // C-bus II PCI bus bridges. + // + Cbus2IrqPolarity[BridgeIndex] = 0xffff; + + Bus = HalpAllocateAndInitPciBusHandler ( + PCIRegInfo->HardwareMechanism & 0xf, + (ULONG)PCIRegInfo->NoBuses, FALSE); + + PCIConfigHandler.Synchronize = Cbus2PCISynchronize; + PCIConfigHandler.ReleaseSynchronzation = + Cbus2PCIReleaseSynchronization; + + Bus->GetInterruptVector = + (PGETINTERRUPTVECTOR) Cbus2GetInterruptVector; + BusData = (PPCIPBUSDATA) Bus->BusData; + BusData->CommonData.Pin2Line = + (PciPin2Line) Cbus2PCIPin2CB2Line; + BusData->CommonData.Line2Pin = + (PciLine2Pin) Cbus2PCICB2Line2Pin; + BusData->GetIrqRange = Cbus2GetFixedPCICB2Line; + Cbus2AdjustSPPBusRange(Bus, BridgeIndex, 1); + + // + // Set the bus numbers for this bridge. + // + Cbus2SetHostBridgeBuses((ULONG)PCIRegInfo->NoBuses, 0, 0, + (ULONG)PCIRegInfo->NoBuses, 0xff); + + // + // Add an additional PCI bus to the registry. + // + Cbus2AddPciBusToRegistry(PCIRegInfo); + + PCIRegInfo->NoBuses++; + + // + // Search the PCI bus on this bus bridge determining + // the hierarchy. + // + Cbus2HierarchicalPciBusSearch(BridgeIndex, PCIRegInfo, + (ULONG)(PCIRegInfo->NoBuses - 1)); + + // + // Set the bus numbers for this bridge. + // + Cbus2SetHostBridgeBuses(Cbus2BridgePCIBusNumber[BridgeIndex], + 0, 0, Cbus2BridgePCIBusNumber[BridgeIndex], + (ULONG)PCIRegInfo->NoBuses - 1); + } +} + +/*++ + +Routine Description: + + Check if this C-bus II system is a PELE system. + If it is a PELE system, it will use a different interrupt + routing algorithm than other C-bus II platforms. + + The identification is done by checking the system board's EISA ID + which is at I/O addresses 0xc80 - 0xc83: + + 1. Write FFh to 0C80h + 2. Read 0C80h + If the contents of 0xc80 equals 0xff, discontinue the + identification process -- the system board does not + have a readable ID. If the contents of 0xc80 does not + equal 0xff and the most significant bit is a zero, + the system board supports a readable ID that can be read + at 0xc80 - 0xc83. + + PELE's ID is "FUJC071", so each byte of the System board ID is as follows: + + 1st byte(0xc80) is 0x1a. + 2nd byte(0xc81) is 0xaa. + 3rd byte(0xc82) is 0xc0. + 4th byte(0xc83) is 0x71. + +Arguments: + + None. + +Return Value: + + Boolean whether a PELE system was found. + +--*/ +UCHAR +Cbus2CheckForPeleSystem( +VOID +) +{ + WRITE_PORT_UCHAR((PUCHAR)0xc80, (UCHAR)0xff); + if (READ_PORT_UCHAR((PUCHAR)0xc80) != (UCHAR)0x1a) + return FALSE; + if (READ_PORT_UCHAR((PUCHAR)0xc81) != (UCHAR)0xaa) + return FALSE; + if (READ_PORT_UCHAR((PUCHAR)0xc82) != (UCHAR)0xc0) + return FALSE; + return TRUE; +} diff --git a/private/ntos/nthals/halcbus/i386/cbus2.h b/private/ntos/nthals/halcbus/i386/cbus2.h new file mode 100644 index 000000000..543d9f1e6 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus2.h @@ -0,0 +1,397 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus2.h + +Abstract: + + Cbus2 hardware architecture definitions for the + Corollary C-bus II multiprocessor HAL modules. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#ifndef _CBUS2_ +#define _CBUS2_ + +// +// General notes: +// +// - ALL reserved fields must be zero filled on writes to +// ensure future compatibility. +// +// - general CSR register length is 64 bits. +// +// + + + +typedef struct _csr_register_t { + + ULONG LowDword; // 32 bit field + ULONG HighDword; // 32 bit field + +} CSR_REGISTER_T, *PCSR_REG; + + + + +typedef union _elementid_t { + struct { + ULONG ElementID : 4; + ULONG Reserved0 : 28; + ULONG Reserved1 : 32; + } ra; + CSR_REGISTER_T rb; +} ELEMENTID_T, *PELEMENTID; + + + + +typedef union _spurious_t { + struct { + ULONG Vector : 8; + ULONG Reserved0 : 24; + ULONG Reserved1 : 32; + } ra; + CSR_REGISTER_T rb; +} SPURIOUS_T; + + +typedef union _windowreloc_t { + struct { + ULONG WindowBase : 9; + ULONG Reserved0 : 23; + ULONG Reserved1 : 32; + } ra; + CSR_REGISTER_T rb; +} WINDOWRELOC_T; + + + + + +// +// The hardware interrupt map table (16 entries) is indexed by irq. +// Lower numerical irq lines will receive higher interrupt (IRQL) priority. +// +// Each CBC has its own hardware interrupt map registers . note that +// each processor gets his own CBC, but it need only be used in this +// mode if there is an I/O card attached to its CBC. each EISA bridge +// will have a CBC, which is used to access any devices on that EISA bus. +// +typedef union _hwintrmap_t { + struct { + ULONG Vector : 8; + ULONG Mode : 3; + ULONG Reserved0 : 21; + ULONG Reserved1 : 32; + } ra; + CSR_REGISTER_T rb; +} HWINTRMAP_T, *PHWINTRMAP; + +/* + * interrupt trigger conditions + */ +#define HW_MODE_DISABLED 0x0 +#define HW_EDGE_RISING 0x100 /* ie: ISA card interrupts */ +#define HW_EDGE_FALLING 0x200 +#define HW_LEVEL_HIGH 0x500 +#define HW_LEVEL_LOW 0x600 + +/* + * CBC rev 1 value for the number of hardware interrupt map entries + */ +#define REV1_HWINTR_MAP_ENTRIES 0x10 + +/* + * max growth for the number of hardware interrupt map entries + */ +#define HWINTR_MAP_ENTRIES 0x20 + + +// +// 256 intrconfig registers for vectors 0 to 255. this determines how +// a given processor will react to each of these vectors. each processor +// has his own intrconfig table in his element space. +// +typedef union _intrconfig_t { + struct { + ULONG Imode : 2; + ULONG Reserved0 : 30; + ULONG Reserved1 : 32; + } ra; + CSR_REGISTER_T rb; +} INTRCONFIG_T, *PINTRCONFIG; + +#define HW_IMODE_DISABLED 0x0 +#define HW_IMODE_ALLINGROUP 0x1 +#define HW_IMODE_LIG 0x2 +#define INTR_CONFIG_ENTRIES 0x100 + +#define CBUS2_LEVEL_TRIGGERED_INTERRUPT 1 +#define CBUS2_EDGE_TRIGGERED_INTERRUPT 2 + + +// +// 256 interrupt request registers for vectors 0 to 255. +// parallel to the interrupt config register set above. +// This is used to send the corresponding interrupt vector. +// which processor gets it is determined by which element's +// address space you write to. +// +// The irq field should always be set when accessed by software. +// only hardware LIG arbitration will clear it. +// +typedef union _intrreq_t { + struct { + ULONG Irq : 1; + ULONG Reserved0 : 31; + ULONG Reserved1 : 32; + } ra; + CSR_REGISTER_T rb; +} INTRREQ_T, *PINTRREQ; + + +// +// the Cbus2 task priority register bit layout and +// minimum/maximum values are defined in cbus.h, as +// they need to be shared with the symmetric Cbus1. +// + +// +// bit 7 of the CBC configuration register must be turned off to enable +// posted writes for EISA I/O cycles. +// +#define CBC_DISABLE_PW 0x80 + + +// +// Offsets of various registers within the processor CSR space. +// +typedef struct _csr { + CSR_REGISTER_T ElementTypeInformation; // 0x0000 + CSR_REGISTER_T IoTypeInformation; // 0x0008 + CSR_REGISTER_T ProcessorReset; // 0x0010 + CSR_REGISTER_T DirectedNmi; // 0x0018 + CSR_REGISTER_T LED; // 0x0020 + + CHAR pad0[0x90 - 0x20 - sizeof (CSR_REGISTER_T)]; + + CSR_REGISTER_T PcBusMemoryHoles0; // 0x0090 + CSR_REGISTER_T PcBusMemoryHoles1; // 0x0098 + CSR_REGISTER_T PcBusMemoryHoles2; // 0x00A0 + CSR_REGISTER_T TLM; // 0x00A8 + + CHAR pad00[0x100 - 0xA8 - sizeof (CSR_REGISTER_T)]; + + ELEMENTID_T BusBridgeSelection; // 0x0100 + + WINDOWRELOC_T BridgeWindow0; // 0x0108 + WINDOWRELOC_T BridgeWindow1; // 0x0110 + + CHAR pad1[0x200 - 0x110 - sizeof (WINDOWRELOC_T)]; + + TASKPRI_T TaskPriority; // 0x0200 + + CSR_REGISTER_T pad2; // 0x208 + CSR_REGISTER_T FaultControl; // 0x210 + CSR_REGISTER_T FaultIndication; // 0x218 + CSR_REGISTER_T InterruptControl; // 0x220 + CSR_REGISTER_T ErrorVector; // 0x228 + CSR_REGISTER_T InterruptIndication; // 0x230 + CSR_REGISTER_T PendingPriority; // 0x238 + + SPURIOUS_T SpuriousVector; // 0x0240 + + CHAR pad3[0x600 - 0x240 - sizeof (CSR_REGISTER_T)]; + + HWINTRMAP_T HardwareInterruptMap[HWINTR_MAP_ENTRIES]; // 0x0600 + CSR_REGISTER_T HardwareInterruptMapEoi[HWINTR_MAP_ENTRIES]; // 0x0700 + INTRCONFIG_T InterruptConfiguration[INTR_CONFIG_ENTRIES]; // 0x0800 + INTRREQ_T InterruptRequest[INTR_CONFIG_ENTRIES]; // 0x1000 + + CHAR pad4[0x2000 - 0x1000 - + INTR_CONFIG_ENTRIES * sizeof (INTRREQ_T)]; + + CSR_REGISTER_T SystemTimer; // 0x2000 + + CHAR pad5[0x2140 - 0x2000 - sizeof(CSR_REGISTER_T)]; + + CSR_REGISTER_T EccError; // 0x2140 + + CHAR pad6[0x3000 - 0x2140 - sizeof(CSR_REGISTER_T)]; + + CSR_REGISTER_T EccClear; // 0x3000 + CSR_REGISTER_T EccSyndrome; // 0x3008 + CSR_REGISTER_T EccWriteAddress; // 0x3010 + CSR_REGISTER_T EccReadAddress; // 0x3018 + + CHAR pad7[0x8000 - 0x3018 - sizeof(CSR_REGISTER_T)]; + + CSR_REGISTER_T CbcConfiguration; // 0x8000 +} CSR_T, *PCSR; + +// +// Offsets of various registers within the memory board CSR space. +// +typedef struct _memcsr { + CHAR pad0[0x10]; + CSR_REGISTER_T MemoryFaultStatus; // 0x0010 + CHAR pad1[0x3020 - 0x10 - sizeof (CSR_REGISTER_T)]; + CSR_REGISTER_T MemoryEccReadAddress; // 0x3020 + CSR_REGISTER_T MemoryEccWriteAddress; // 0x3028 + CSR_REGISTER_T MemoryEccClear; // 0x3028 + CSR_REGISTER_T MemoryEccSyndrome; // 0x3030 +} MEMCSR_T, *PMEMCSR; + +// +// Interrupt Indication register bit that a fault occurred. This applies +// to the processor CSR. +// +#define CBUS2_FAULT_DETECTED 0x2 + +// +// Fault Indication register bits for errors this processor's CSR has latched. +// +#define CBUS2_BUS_DATA_UNCORRECTABLE 0x2 +#define CBUS2_BUS_ADDRESS_UNCORRECTABLE 0x10 + +// +// Fault Status register bit for errors latched by this memory board's CSR. +// +#define CBUS2_MEMORY_FAULT_DETECTED 0x80 + +// +// RRD will provide an entry for every Cbus2 element. To avoid +// using an exorbitant number of PTEs, this entry will specify +// only the CSR space within each Cbus2 element's space. And only +// a subset of that, as well, usually on the order of 4 pages. +// Code wishing to access other portions of the cbus_element +// space will need to subtract down from the RRD-specified CSR +// address and map according to their particular needs. +// +#define MAX_CSR_BYTES 0x10000 + +#define csr_register rb.LowDword + +// +// The full layout of a cbus2 element space: +// +// Note that only the Csr offset/size is passed up by RRD, and +// mapped by the HAL. before referencing any other fields, the +// caller must first set up appropriate PDE/PTE entries. +// +typedef struct _cbus2element { + CHAR PCBusRAM [16 * 1024 * 1024]; + CHAR PCBusWindow0 [8 * 1024 * 1024]; + CHAR PCBusWindow1 [8 * 1024 * 1024]; + CHAR DeviceReserved [28 * 1024 * 1024]; + CHAR ControlIO [0x10000]; + CHAR PCBusIO [0x10000]; + CHAR Csr [MAX_CSR_BYTES]; + CHAR IdentityMappedCsr [MAX_CSR_BYTES]; + CHAR Reserved [0x1C0000]; + CHAR Ram [0x100000]; + CHAR Prom [0x100000]; +} CBUS2_ELEMENT, *PCBUS2_ELEMENT; + +// +// offset of CSR within any element's I/O space. +// +#define CBUS_CSR_OFFSET 0x3C20000 + +// +// id of the default C-bus II bridge +// +#define CBUS2_DEFAULT_BRIDGE_ID 0 + +// +// reserved element types +// +#define CBUS2_ELEMENT_TYPE_RAS 0x50 + +// +// Shift for PC Bus Window CSRs +// +#define CBUS2_WINDOW_REGISTER_SHIFT 23 + +// +// PC Bus memory hole defines to designate all memory +// holes go to system memory. +// +#define CBUS2_NO_MEMORY_HOLES 0x3f; +#define CBUS2_NO_MEMORY_HOLES1 0xff; +#define CBUS2_NO_MEMORY_HOLES2 0xffff; + +// +// turn the LED on or off. +// +#define CBUS2_LED_OFF 0 +#define CBUS2_LED_ON 1 + +// +// +// Physical address of the Cbus2 local APIC +// +#define CBUS2_LOCAL_APIC_LOCATION (PVOID)0xFEE00000 + +// +// Physical address of the Cbus2 I/O APIC +// +#define CBUS2_IO_APIC_LOCATION (PVOID)0xFEC00000 + +// +// Programmed I/O translation ranges +// +#define CBUS2_IO_BASE_ADDRESS 0x0000 +#define CBUS2_IO_LIMIT 0xFFFF + +// +// PCI macros used to fill in the configuration space of a PPB +// +#define PCI_IO_TO_CFG(x) ((UCHAR)(((x) & 0xF000) >> 8)) +#define PCI_MEMORY_TO_CFG(x) ((USHORT)(((x) & 0xFFF00000) >> 16)) +#define PCI_PREFETCH_TO_CFG(x) ((USHORT)(((x) & 0xFFF00000) >> 16)) + +#define CBUS2_PCI_LATENCY_TIMER 0x60 + +typedef struct _cbus2_host_bridge_config { + USHORT VendorID; // (ro) 0x0000 + USHORT DeviceID; // (ro) 0x0002 + USHORT Command; // Device control 0x0004 + USHORT Status; // 0x0006 + UCHAR RevisionID; // (ro) 0x0008 + UCHAR ProgIf; // (ro) 0x0009 + UCHAR SubClass; // (ro) 0x000A + UCHAR BaseClass; // (ro) 0x000B + UCHAR CacheLineSize; // (ro+) 0x000C + UCHAR LatencyTimer; // (ro+) 0x000D + UCHAR HeaderType; // (ro) 0x000E + UCHAR BIST; // Built in self test 0x000F + + UCHAR pad1[0x4A - 0XF - sizeof (UCHAR)]; + UCHAR BusNumber; // 0x004A + UCHAR SubordinateBusNumber; // 0x004B + UCHAR pad2[0x70 - 0X4B - sizeof (UCHAR)]; + UCHAR ErrorCommand; // 0x0070 + UCHAR ErrorStatus; // 0x0071 + UCHAR MasterCommand; // 0x0072 + UCHAR pad3; + UCHAR DiagnosticMode; // 0x0074 + UCHAR pad4[0xFF - 0x74 - sizeof (UCHAR)]; +} CBUS2_HOST_BRIDGE_CONFIG, *PCBUS2_HOST_BRIDGE_CONFIG; + +#endif // _CBUS2_ diff --git a/private/ntos/nthals/halcbus/i386/cbus2cbc.asm b/private/ntos/nthals/halcbus/i386/cbus2cbc.asm new file mode 100644 index 000000000..a69cc8c39 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus2cbc.asm @@ -0,0 +1,283 @@ + title "Software Interrupts" +;++ +; +;Copyright (c) 1992, 1993, 1994 Corollary Inc +; +;Module Name: +; +; cbuscbc.asm +; +;Abstract: +; +; This module implements the Corollary Cbus2 HAL routines to deal +; with the Corollary CBC distributed interrupt controller chip. +; +; This includes the_sending_ of software and IPI interrupts in Windows NT. +; The receipt of these interrupts is handled elsewhere. +; +; Note: The routines in this module are jmp'ed to directly from +; their common Hal counterparts. +; +;Author: +; +; Landy Wang (landy@corollary.com) 26-Mar-1992 +; +;Environment: +; +; Kernel Mode +; +;Revision History: +; +;-- + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include cbus.inc + +; +; Some definitions needed for accessing the Corollary CBC... +; + +INTS_ENABLED equ 200 ; X86 EFLAGS bit definition + + .list + +_TEXT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; Cbus2RequestSoftwareInterrupt ( +; IN KIRQL RequestIrql +; ) +; +; Routine Description: +; +; This routine is used to issue a software interrupt to the +; calling processor. Since this is all done in hardware, the +; code to implement this is trivial. Our hardware supports +; sending the interrupt to lowest-in-group processors, which +; would be useful for a good number of DPCs, for example, but +; the kernel doesn't currently tell us which kinds of software +; interrupts need to go to the caller versus which can go to +; any processor. +; +; Arguments: +; +; (esp+4) = RequestIrql - Supplies the request IRQL value +; +; Return Value: +; +; None. +; +;-- + +; +; equates for accessing arguments +; + +KsiRequestIrql equ byte ptr [esp+4] + +cPublicProc _Cbus2RequestSoftwareInterrupt ,1 + + xor ecx, ecx ; (faster than movzx) + mov cl, KsiRequestIrql ; to get the irql + + mov eax, [_Cbus2IrqlToCbus2Addr+4*ecx] ; get h/w CSR offset +ifdef CBC_REV1 + pushfd + cli + add eax, PCR[PcHal.PcrCSR] ; get h/w CSR base + + mov dword ptr [eax], 1 ; send the interrupt + popfd +else + mov dword ptr [eax], 1 ; send the interrupt +endif + + stdRET _Cbus2RequestSoftwareInterrupt +stdENDP _Cbus2RequestSoftwareInterrupt + + page ,132 + subttl "Cbus2RequestIpi" +;++ +; +; VOID +; Cbus2RequestIpi( +; IN ULONG Mask +; ); +; +; Routine Description: +; +; Requests an interprocessor interrupt. +; +; This is generally not an easy thing in Cbus2 hardware... +; +; a) Interrupting everyone incl yourself: EASY IN HARDWARE +; +; b) Interrupting everyone but yourself: DIFFICULT IN HARDWARE, +; MADE EASY BY SOFTWARE +; +; c) Interrupting a random processor subset: DIFFICULT IN HARDWARE, +; NOT EASY FOR SOFTWARE EITHER, +; RESULTS IN LOOPING BELOW +; +; +; To deal with case b), a set of MAX_CBUS_ELEMENTS interrupts have +; been allocated for IPI vectors. Each processor participates in ALL of them +; EXCEPT one. So for any processor to issue a global broadcast to all +; the others, he just sends the IPI vector which he isn't participating in. +; +; To support case c) using the case b) model would result in too many +; vectors being allocated, so instead we loop here in software to do it. +; +; Arguments: +; +; Mask - Mask of processors to be interrupted +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _Cbus2RequestIpi ,1 + mov edx, [esp+4] ; get requested recipients + + cmp [_Cbus2EnableBroadcast], 1 + jne short somesubset + + cmp edx, PCR[PcHal.PcrAllOthers] ; broadcast to everyone else? + jne short somesubset ; no, some subset of processors + + mov edx, PCR[PcHal.PcrBroadcast] ; get h/w addr of all others + mov dword ptr [edx], 1 ; interrupt them all + + stdRET _Cbus2RequestIpi + + ; + ; somewhat unwieldy to structure the code this way, but + ; it avoids the expensive bsr/bsf. to avoid a 64K array + ; of processor numbers, do it in two separate passes, + ; first do processors 0 through 7, and then processors 8 + ; through 0xF. + ; + + align 4 +somesubset: + + or dl, dl ; any processors 0..7 ? + jz highcpus ; no, check processors 8..F + + align 4 +@@: + movzx ecx, dl ; set up working copy + mov cl, _HalpFindFirstSetRight[ecx] ; get processor number + + mov eax, 1 + shl eax, cl + xor edx, eax ; clear bit in requested mask + + ; get correct IPI address + mov ecx, dword ptr [_Cbus2SendIPI + ecx * 4] + + mov dword ptr [ecx], 1 ; send this processor the IPI + + or dl, dl ; any more processors 0..7 ? + jnz short @b ; get next 0..7 processor + + align 4 +highcpus: + or dh, dh ; any processors 8..F? + jz alldone ; no, all done + shr edx, 8 ; check high processors in dl + + align 4 +@@: + movzx ecx, dl ; set up working copy + mov cl, _HalpFindFirstSetRight[ecx] ; get (processor number - 8) + + mov eax, 1 + shl eax, cl + xor edx, eax ; clear bit in requested mask + + add ecx, 8 ; in second set of processors + + ; get correct IPI address + mov ecx, [_Cbus2SendIPI + ecx * 4 ] + + mov dword ptr [ecx], 1 ; send this processor the IPI + + or dl, dl ; any more processors 0..7 ? + jnz short @b ; get next 0..7 processor + + align 4 +alldone: + + stdRET _Cbus2RequestIpi +stdENDP _Cbus2RequestIpi + +; +; ULONG +; Cbus2ReadCSR( +; ULONG CsrAddress +; ) +; +; Routine Description: +; +; Read the specified register in the CSR space. This routine is +; coded in assembly because the register must be read/written 32 +; bits at a time, and we don't want the compiler "optimizing" our +; accesses into byte-enabled operations which the hardware won't +; understand. +; +; Arguments: +; (esp+4) = Address of the CSR register +; +; Returns: +; Value of the register. +; +;-- +cPublicProc _Cbus2ReadCSR ,1 + mov ecx, [esp + 4] ; CSR register address + mov eax, dword ptr [ecx] ; return CSR register contents + stdRET _Cbus2ReadCSR +stdENDP _Cbus2ReadCSR + +;++ +; +; VOID +; Cbus2WriteCSR( +; ULONG CsrAddress +; ) +; +; Routine Description: +; +; Write the specified register in the CSR space. This routine is +; coded in assembly because the register must be read/written 32 +; bits at a time, and we don't want the compiler "optimizing" our +; accesses into byte-enabled operations which the hardware won't +; understand. +; +; Arguments: +; (esp+4) = Address of the CSR register +; (esp+8) = Contents to write to the specified register +; +;-- +cPublicProc _Cbus2WriteCSR ,2 + + mov ecx, [esp + 4] ; CSR register address + mov eax, [esp + 8] ; new contents + mov [ecx], eax ; set the new value + + stdRET _Cbus2WriteCSR + +stdENDP _Cbus2WriteCSR + +_TEXT ENDS + END diff --git a/private/ntos/nthals/halcbus/i386/cbus2ecc.c b/private/ntos/nthals/halcbus/i386/cbus2ecc.c new file mode 100644 index 000000000..f19655623 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus2ecc.c @@ -0,0 +1,475 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus2ecc.c + +Abstract: + + This module implements the Corollary Cbus2 ECC specific functions for + the Hardware Architecture Layer (HAL) for Windows NT. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + + +--*/ + +#include "halp.h" +#include "cbusrrd.h" // HAL <-> RRD interface definitions +#include "cbus_nt.h" // C-bus NT-specific implementation stuff +#include "cbus.h" // needed for cbus2.h inclusion +#include "cbus2.h" // C-bus II specific stuff +#include "bugcodes.h" +#include "stdio.h" +#include "cbusnls.h" + +ULONG +Cbus2ReadCSR(PULONG); + +VOID +Cbus2WriteCSR(PULONG, ULONG); + +VOID +CbusHardwareFailure( +IN PUCHAR HardwareMessage +); + +extern KSPIN_LOCK Cbus2NMILock; +extern ULONG CbusBootedProcessors; +extern ULONG Cbus2BridgesFound; +extern PCSR Cbus2BridgeCSR[CBUS_MAX_BRIDGES]; +ULONG Cbus2NMIHandler; + +// +// defines for the Cbus2 ECC syndrome +// +#define MULTIBIT 3 +#define DOUBLEBIT 2 +#define SINGLEBIT 1 +#define NOECCERROR 0x7f + +// +// defines for the Cbus2 ECC error register +// +typedef struct _extmear_t { + ULONG Syndrome:8; + ULONG reserved:24; +} EXTMEAR_T, *PEXTMEAR; + +UCHAR cbus2_edac_syndrome[] = { + +NOECCERROR,/* 00 */ SINGLEBIT, /* 01 */ SINGLEBIT, /* 02 */ MULTIBIT, /* 03 */ +SINGLEBIT, /* 04 */ MULTIBIT, /* 05 */ MULTIBIT, /* 06 */ MULTIBIT, /* 07 */ +SINGLEBIT, /* 08 */ MULTIBIT, /* 09 */ MULTIBIT, /* 0A */ SINGLEBIT, /* 0B */ +MULTIBIT, /* 0C */ MULTIBIT, /* 0D */ SINGLEBIT, /* 0E */ MULTIBIT, /* 0F */ +SINGLEBIT, /* 10 */ MULTIBIT, /* 11 */ MULTIBIT, /* 12 */ SINGLEBIT, /* 13 */ +MULTIBIT, /* 14 */ SINGLEBIT, /* 15 */ SINGLEBIT, /* 16 */ MULTIBIT, /* 17 */ +MULTIBIT, /* 18 */ SINGLEBIT, /* 19 */ SINGLEBIT, /* 1A */ MULTIBIT, /* 1B */ +SINGLEBIT, /* 1C */ MULTIBIT, /* 1D */ MULTIBIT, /* 1E */ MULTIBIT, /* 1F */ +SINGLEBIT, /* 20 */ MULTIBIT, /* 21 */ MULTIBIT, /* 22 */ SINGLEBIT, /* 23 */ +MULTIBIT, /* 24 */ SINGLEBIT, /* 25 */ SINGLEBIT, /* 26 */ MULTIBIT, /* 27 */ +MULTIBIT, /* 28 */ SINGLEBIT, /* 29 */ SINGLEBIT, /* 2A */ MULTIBIT, /* 2B */ +SINGLEBIT, /* 2C */ MULTIBIT, /* 2D */ MULTIBIT, /* 2E */ MULTIBIT, /* 2F */ +MULTIBIT, /* 30 */ SINGLEBIT, /* 31 */ MULTIBIT, /* 32 */ MULTIBIT, /* 33 */ +SINGLEBIT, /* 34 */ MULTIBIT, /* 35 */ MULTIBIT, /* 36 */ MULTIBIT, /* 37 */ +MULTIBIT, /* 38 */ MULTIBIT, /* 39 */ MULTIBIT, /* 3A */ MULTIBIT, /* 3B */ +MULTIBIT, /* 3C */ MULTIBIT, /* 3D */ MULTIBIT, /* 3E */ MULTIBIT, /* 3F */ +SINGLEBIT, /* 40 */ MULTIBIT, /* 41 */ MULTIBIT, /* 42 */ MULTIBIT, /* 43 */ +MULTIBIT, /* 44 */ MULTIBIT, /* 45 */ MULTIBIT, /* 46 */ MULTIBIT, /* 47 */ +MULTIBIT, /* 48 */ MULTIBIT, /* 49 */ SINGLEBIT, /* 4A */ MULTIBIT, /* 4B */ +MULTIBIT, /* 4C */ MULTIBIT, /* 4D */ MULTIBIT, /* 4E */ SINGLEBIT, /* 4F */ +MULTIBIT, /* 50 */ MULTIBIT, /* 51 */ SINGLEBIT, /* 52 */ MULTIBIT, /* 53 */ +SINGLEBIT, /* 54 */ MULTIBIT, /* 55 */ MULTIBIT, /* 56 */ SINGLEBIT, /* 57 */ +SINGLEBIT, /* 58 */ MULTIBIT, /* 59 */ MULTIBIT, /* 5A */ SINGLEBIT, /* 5B */ +MULTIBIT, /* 5C */ SINGLEBIT, /* 5D */ MULTIBIT, /* 5E */ MULTIBIT, /* 5F */ +MULTIBIT, /* 60 */ MULTIBIT, /* 61 */ SINGLEBIT, /* 62 */ MULTIBIT, /* 63 */ +SINGLEBIT, /* 64 */ MULTIBIT, /* 65 */ MULTIBIT, /* 66 */ SINGLEBIT, /* 67 */ +SINGLEBIT, /* 68 */ MULTIBIT, /* 69 */ MULTIBIT, /* 6A */ SINGLEBIT, /* 6B */ +MULTIBIT, /* 6C */ SINGLEBIT, /* 6D */ MULTIBIT, /* 6E */ MULTIBIT, /* 6F */ +SINGLEBIT, /* 70 */ MULTIBIT, /* 71 */ MULTIBIT, /* 72 */ MULTIBIT, /* 73 */ +MULTIBIT, /* 74 */ SINGLEBIT, /* 75 */ MULTIBIT, /* 76 */ MULTIBIT, /* 77 */ +MULTIBIT, /* 78 */ MULTIBIT, /* 79 */ MULTIBIT, /* 7A */ MULTIBIT, /* 7B */ +MULTIBIT, /* 7C */ MULTIBIT, /* 7D */ MULTIBIT, /* 7E */ MULTIBIT, /* 7F */ +SINGLEBIT, /* 80 */ MULTIBIT, /* 81 */ MULTIBIT, /* 82 */ MULTIBIT, /* 83 */ +MULTIBIT, /* 84 */ MULTIBIT, /* 85 */ MULTIBIT, /* 86 */ MULTIBIT, /* 87 */ +MULTIBIT, /* 88 */ MULTIBIT, /* 89 */ SINGLEBIT, /* 8A */ MULTIBIT, /* 8B */ +MULTIBIT, /* 8C */ MULTIBIT, /* 8D */ MULTIBIT, /* 8E */ SINGLEBIT, /* 8F */ +MULTIBIT, /* 90 */ MULTIBIT, /* 91 */ SINGLEBIT, /* 92 */ MULTIBIT, /* 93 */ +SINGLEBIT, /* 94 */ MULTIBIT, /* 95 */ MULTIBIT, /* 96 */ SINGLEBIT, /* 97 */ +SINGLEBIT, /* 98 */ MULTIBIT, /* 99 */ MULTIBIT, /* 9A */ SINGLEBIT, /* 9B */ +MULTIBIT, /* 9C */ SINGLEBIT, /* 9D */ MULTIBIT, /* 9E */ MULTIBIT, /* 9F */ +MULTIBIT, /* A0 */ MULTIBIT, /* A1 */ SINGLEBIT, /* A2 */ MULTIBIT, /* A3 */ +SINGLEBIT, /* A4 */ MULTIBIT, /* A5 */ MULTIBIT, /* A6 */ SINGLEBIT, /* A7 */ +SINGLEBIT, /* A8 */ MULTIBIT, /* A9 */ MULTIBIT, /* AA */ SINGLEBIT, /* AB */ +MULTIBIT, /* AC */ SINGLEBIT, /* AD */ MULTIBIT, /* AE */ MULTIBIT, /* AF */ +SINGLEBIT, /* B0 */ MULTIBIT, /* B1 */ MULTIBIT, /* B2 */ MULTIBIT, /* B3 */ +MULTIBIT, /* B4 */ SINGLEBIT, /* B5 */ MULTIBIT, /* B6 */ MULTIBIT, /* B7 */ +MULTIBIT, /* B8 */ MULTIBIT, /* B9 */ MULTIBIT, /* BA */ MULTIBIT, /* BB */ +MULTIBIT, /* BC */ MULTIBIT, /* BD */ MULTIBIT, /* BE */ MULTIBIT, /* BF */ +MULTIBIT, /* C0 */ MULTIBIT, /* C1 */ MULTIBIT, /* C2 */ MULTIBIT, /* C3 */ +MULTIBIT, /* C4 */ MULTIBIT, /* C5 */ MULTIBIT, /* C6 */ MULTIBIT, /* C7 */ +MULTIBIT, /* C8 */ MULTIBIT, /* C9 */ MULTIBIT, /* CA */ SINGLEBIT, /* CB */ +MULTIBIT, /* CC */ MULTIBIT, /* CD */ SINGLEBIT, /* CE */ MULTIBIT, /* CF */ +MULTIBIT, /* D0 */ MULTIBIT, /* D1 */ MULTIBIT, /* D2 */ SINGLEBIT, /* D3 */ +MULTIBIT, /* D4 */ SINGLEBIT, /* D5 */ SINGLEBIT, /* D6 */ MULTIBIT, /* D7 */ +MULTIBIT, /* D8 */ SINGLEBIT, /* D9 */ SINGLEBIT, /* DA */ MULTIBIT, /* DB */ +SINGLEBIT, /* DC */ MULTIBIT, /* DD */ MULTIBIT, /* DE */ MULTIBIT, /* DF */ +MULTIBIT, /* E0 */ MULTIBIT, /* E1 */ MULTIBIT, /* E2 */ SINGLEBIT, /* E3 */ +MULTIBIT, /* E4 */ SINGLEBIT, /* E5 */ SINGLEBIT, /* E6 */ MULTIBIT, /* E7 */ +MULTIBIT, /* E8 */ SINGLEBIT, /* E9 */ SINGLEBIT, /* EA */ MULTIBIT, /* EB */ +SINGLEBIT, /* EC */ MULTIBIT, /* ED */ MULTIBIT, /* EE */ MULTIBIT, /* EF */ +MULTIBIT, /* F0 */ SINGLEBIT, /* F1 */ MULTIBIT, /* F2 */ MULTIBIT, /* F3 */ +SINGLEBIT, /* F4 */ MULTIBIT, /* F5 */ MULTIBIT, /* F6 */ MULTIBIT, /* F7 */ +MULTIBIT, /* F8 */ MULTIBIT, /* F9 */ MULTIBIT, /* FA */ MULTIBIT, /* FB */ +MULTIBIT, /* FC */ MULTIBIT, /* FD */ MULTIBIT, /* FE */ MULTIBIT, /* FF */ + +}; + +#if DBG +#define NMI_BUTTON_PRESSED() 0 +#else +#define NMI_BUTTON_PRESSED() 0 +#endif + +NTSTATUS +Cbus2ResolveNMI( + IN PVOID NmiInfo + ) +/*++ + +Routine Description: + + This function determines the cause of the NMI so that the user can + replace any offending SIMMs. + +Arguments: + + NmiInfo - pointer to the NMI information structure + +Return Value: + + Returns the byte address which caused the NMI, 0 if indeterminate + +--*/ +{ + PCSR csr; + UCHAR syndrome; + UCHAR memsyndrome; + UCHAR EccError; + ULONG Processor; + ULONG InterruptIndication; + ULONG FaultIndication; + ULONG ErrorType; + PMEMCSR memcsr; + PMEMORY_CARD pm; + ULONG board; + UCHAR NmiMessage[80]; + PHYSICAL_ADDRESS FaultAddress; + BOOLEAN founderror = FALSE; + ULONG original_bridge; + ULONG BridgeId; + ULONG BusNumber; + extern ULONG Cbus2BridgeId[]; + extern PCSR Cbus2BridgeCSR[]; + extern NTSTATUS DefaultHalHandleNMI( IN OUT PVOID); + extern VOID CbusClearEISANMI(VOID); + + if (NMI_BUTTON_PRESSED()) { + + // + // NMI button was pressed, so go to the debugger + // + + _asm { + int 3 + } + + // + // Clear the NMI in hardware so the system can continue + // + // assume that bridge 0 needs the clear in this case. + // save the original value for restoral after the clear. + // repoint our I/O references to the default bus bridge number. + // + BusNumber =0; + + BridgeId = Cbus2BridgeId[BusNumber]; + csr = Cbus2BridgeCSR[BusNumber]; + + original_bridge = csr->BusBridgeSelection.csr_register; + csr->BusBridgeSelection.csr_register = + ((original_bridge & ~MAX_ELEMENT_CSRS) | BridgeId); + + + CbusClearEISANMI(); + + // + // restore our default bridge references to what they + // were when we started... + // + csr->BusBridgeSelection.csr_register = original_bridge; + + return STATUS_SUCCESS; // ignore this NMI + } + + if (CbusGlobal.nonstdecc) + return DefaultHalHandleNMI(NmiInfo); + + // + // All Cbus2 faults will generate an NMI on all the processors. + // An EISA NMI will also go to all CPUs. Only directed NMIs + // (sent by software) can go to less than all the processors. + // + // only one processor may proceed beyond this point, + // so first get the Cbus HAL's NMI lock. + // + + KiAcquireSpinLock(&Cbus2NMILock); + + if (Cbus2NMIHandler) { + KiReleaseSpinLock(&Cbus2NMILock); + // + // another processor is handling it, so just spin forever + // + while (1) + ; + } + + Cbus2NMIHandler = 1; + + KiReleaseSpinLock(&Cbus2NMILock); + + // + // Now display all the CSRs of: + // a) all the processors and + // b) all the memory boards and + // c) all the I/O bridges + // + + // + // print out a leading newline so if he's running a checked + // build, he'll be able to see the whole line. otherwise, + // the kernel debugger CTS/SEND/etc. serial line status will + // overwrite the first NMI line from our processor loop below. + // + HalDisplayString (MSG_NEWLINE); + + // + // first go through the processors. there is no need to disable + // ecc to safely take the system down because we will not iret, + // (which is needed to re-enable NMI). + // + for (Processor = 0; Processor < CbusBootedProcessors; Processor++) { + csr = CbusCSR[Processor].csr; + + InterruptIndication = csr->InterruptIndication.LowDword; + + // + // if the interrupt indication is not set, then it's not + // a Cbus2 NMI, so it must be something from EISA. we'll + // handle EISA NMI detection last. + // + if ((InterruptIndication & CBUS2_FAULT_DETECTED) == 0) { + sprintf(NmiMessage, MSG_NMI_ECC0, Processor); + HalDisplayString (NmiMessage); + continue; + } + + founderror = TRUE; + FaultIndication = (csr->FaultIndication.LowDword & 0xFF); + + if ((FaultIndication & (CBUS2_BUS_DATA_UNCORRECTABLE | CBUS2_BUS_ADDRESS_UNCORRECTABLE)) == 0) { + // + // it is a Cbus2 NMI, but we cannot determine the + // address. at least display the fault indication + // register so we can see what type of error it was. + // + sprintf(NmiMessage, MSG_NMI_ECC1, Processor, + FaultIndication & CbusGlobal.FaultControlMask); + HalDisplayString (NmiMessage); + continue; + } + + FaultAddress.LowPart = 0; + FaultAddress.HighPart = 0; + + // + // EccError contains the quadword index of which quadword + // in the cache line is bad. since words in a cacheline + // are not always transferred in order, we must print this + // value as well as the address of the cacheline. the + // transfer order is deterministic based on the specific + // addresses, but not all addresses are read in the same order. + // + EccError = ((UCHAR)csr->EccError.LowDword & 0x03); + + syndrome = ((UCHAR)csr->EccSyndrome.LowDword & 0xFF); + + // + // check if this memory board generated the ecc error + // + + ErrorType = cbus2_edac_syndrome[syndrome]; + + ASSERT (ErrorType != NOECCERROR && ErrorType != SINGLEBIT); + + // + // the error is latched in this processor's DPX registers. + // now we need to figure out which register is correct, since + // the error could have happened in the memory or on the bus. + // + + // + // display the values this processor has latched. + // + FaultAddress.HighPart = csr->EccWriteAddress.LowDword; + FaultAddress.LowPart = csr->EccReadAddress.LowDword; + + sprintf(NmiMessage, MSG_NMI_ECC2, + Processor, + FaultAddress.HighPart, + FaultAddress.LowPart, + EccError, + FaultIndication & CbusGlobal.FaultControlMask); + + HalDisplayString (NmiMessage); + } + + // + // next go through the memory boards... + // + + pm = CbusMemoryBoards; + for (board = 0; board < CbusMemoryBoardIndex; board++, pm++) { + + memcsr = (PMEMCSR)pm->regmap; + + if ((memcsr->MemoryFaultStatus.LowDword & CBUS2_MEMORY_FAULT_DETECTED) == 0) { + sprintf(NmiMessage, MSG_NMI_ECC3, board); + HalDisplayString (NmiMessage); + continue; + } + + founderror = TRUE; + + // + // this board contains an error + // + memsyndrome = ((UCHAR)memcsr->MemoryEccSyndrome.LowDword & 0xFF); + ErrorType = cbus2_edac_syndrome[memsyndrome]; + + ASSERT (ErrorType != NOECCERROR && ErrorType != SINGLEBIT); + + FaultAddress.HighPart = memcsr->MemoryEccWriteAddress.LowDword; + FaultAddress.LowPart = memcsr->MemoryEccReadAddress.LowDword; + + sprintf(NmiMessage, MSG_NMI_ECC4, + board, + FaultAddress.HighPart, + FaultAddress.LowPart); + + HalDisplayString (NmiMessage); + } + + // + // lastly, go through the I/O bridges... + // + for (BusNumber = 0; BusNumber < Cbus2BridgesFound; BusNumber++) { + csr = Cbus2BridgeCSR[BusNumber]; + + InterruptIndication = csr->InterruptIndication.LowDword; + + // + // if the interrupt indication is not set, then it's not + // a Cbus2 NMI, so it must be something from EISA. we'll + // handle EISA NMI detection last. + // + if ((InterruptIndication & CBUS2_FAULT_DETECTED) == 0) { + sprintf(NmiMessage, MSG_NMI_ECC5, BusNumber); + HalDisplayString (NmiMessage); + continue; + } + + founderror = TRUE; + FaultIndication = (csr->FaultIndication.LowDword & 0xFF); + + if ((FaultIndication & (CBUS2_BUS_DATA_UNCORRECTABLE | CBUS2_BUS_ADDRESS_UNCORRECTABLE)) == 0) { + // + // it is a Cbus2 NMI, but we cannot determine the + // address. at least display the fault indication + // register so we can see what type of error it was. + // + sprintf(NmiMessage, MSG_NMI_ECC6, BusNumber, + FaultIndication & CbusGlobal.FaultControlMask); + HalDisplayString (NmiMessage); + continue; + } + + FaultAddress.LowPart = 0; + FaultAddress.HighPart = 0; + + // + // EccError contains the quadword index of which quadword + // in the cache line is bad. since words in a cacheline + // are not always transferred in order, we must print this + // value as well as the address of the cacheline. the + // transfer order is deterministic based on the specific + // addresses, but not all addresses are read in the same order. + // + EccError = ((UCHAR)csr->EccError.LowDword & 0x03); + + syndrome = ((UCHAR)csr->EccSyndrome.LowDword & 0xFF); + + // + // check if this memory board generated the ecc error + // + + ErrorType = cbus2_edac_syndrome[syndrome]; + + ASSERT (ErrorType != NOECCERROR && ErrorType != SINGLEBIT); + + // + // the error is latched in this processor's DPX registers. + // now we need to figure out which register is correct, since + // the error could have happened in the memory or on the bus. + // + + // + // display the values this processor has latched. + // + FaultAddress.HighPart = csr->EccWriteAddress.LowDword; + FaultAddress.LowPart = csr->EccReadAddress.LowDword; + + sprintf(NmiMessage, MSG_NMI_ECC7, + BusNumber, + FaultAddress.HighPart, + FaultAddress.LowPart, + EccError, + FaultIndication & CbusGlobal.FaultControlMask); + + HalDisplayString (NmiMessage); + } + + if (founderror == TRUE) { + // + // this call will not return + // + CbusHardwareFailure (MSG_NMI_ECC8); + } + + // + // No errors were found in Cbus RAM, so check for EISA errors + // + + DefaultHalHandleNMI(NmiInfo); +} diff --git a/private/ntos/nthals/halcbus/i386/cbus_nt.h b/private/ntos/nthals/halcbus/i386/cbus_nt.h new file mode 100644 index 000000000..258e391de --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus_nt.h @@ -0,0 +1,226 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus_nt.h + +Abstract: + + Windows NT-specific definitions for the Corollary C-bus I & II + MP HAL modules. The global definitions needed for the + Windows NT HAL reside here. Hardware architecture-specific + definitions are in their respective modules. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#ifndef _CBUS_NT_H +#define _CBUS_NT_H + +// +// WARNING: Changes to the CBUS_MAX_BRIDGES definition below may involve +// detailed changes to the source code which allocates interrupt vectors. +// Make sure you know what you are doing if you change this. +// + +#define CBUS_MAX_BRIDGES 2 + +extern ULONG CbusProcessors; + +extern PVOID CbusBroadcastCSR; + +// +// Map IRQL level to a hardware task priority vector. Note +// that since many drivers may share the same IRQL, yet have +// different hardware interrupt vectors. Thus, the CbusIrqlToVector[] +// array will always contain the highest priority (== highest numerically) +// vector for a given IRQL index. This way, CbusIrqlToVector[] can provide +// fast lookups for KfRaiseIrql(), KfLowerIrql(), etc. +// +extern ULONG CbusIrqlToVector[HIGH_LEVEL + 1]; + +// +// The first 0x30 IDT entries are reserved as follows: +// 0 -- 0xf for Intel processor traps +// 0x10 -- 0x2f for Intel reserved +// 0x20 -- 0x2f for Microsoft (system call entry point, profiling, etc) +// +// APC is the lowest priority interrupt actually sent & masked, and Microsoft +// has reserved vector 0x1F for it. DPC has been reserved vector 0x2F by +// Microsoft. +// +// All other task priorities and interrupt request registers cannot +// include the low 0x30 IDT entries. Vectors and task priorities are +// synonymous to the Corollary hardware. Cbus1 & Cbus2 share +// the LOW, APC, DPC and HIGH task priorities. Other priorities generally +// differ depending on whether the CBC or the APIC is being used. This +// is because the CBC allows byte priority granularity, but the APIC only +// prioritizes in buckets of 16 priorities. The individual hardware modules +// contain the exact architecture layout and issues. +// + +#define LOW_TASKPRI 0x00 // lowest priority +#define APC_TASKPRI 0x1F +#define DPC_TASKPRI 0x2F +#define HIGH_TASKPRI 0xFF // highest priority + +// +// Define the PCR HalReserved[] entries being used by the Corollary HAL. +// +// WARNING! THIS MUST MATCH cbus.inc !!! +// + +// +// element is the logical processor number (0..n) +// + +#define PCR_PROCESSOR 0 + +// +// bit is the logical processor bit number (0x1, 0x2, 0x4, 0x8, 0x10, etc) +// + +#define PCR_BIT 1 + +// +// PCR_CSR maps the per-processor area that RRD has informed us about: +// for Cbus2, this CPUS's csr base pointer +// for Cbus1, this CPUS's memory-mapped Cbus I/O space + +#define PCR_CSR 2 + +// +// Cbus2 CBC & Cbus1 APIC taskpri reg address +// Cbus1 taskpri didn't need to be in PCR +// since we have mapped the APIC at the same +// PHYSICAL address for all processors, but +// doing it this way achieves more commonality +// between the code for the two platforms. +// For CBC Rev2, we will use the identity mapped +// CSR and completely get rid of this field. +// + +#define PCR_TASKPRI 3 + +// +// CBC global broadcast hardware address - provides a way for any +// processor to interrupt everyone but himself. This entry is not used +// by the APIC code, as the APIC supports this completely in hardware. +// + +#define PCR_BROADCAST 4 + +// +// Bitfield mask of all the other processors in the system but the caller +// +#define PCR_ALL_OTHERS 5 + +// +// Address of this processor's LED ON register +// +#define PCR_LED_ON 6 + +// +// Address of this processor's LED OFF register +// +#define PCR_LED_OFF 7 + +// +// This PCR field will be used by all CPUs other than CPU 0 to find out +// when to call the KeUpdateRunTime() routine. This routine (KeUpdateRunTime) +// is called at the maximum supported rate as reported by KeSetTimeIncrement. +// +#define TICK_OFFSET 8 + +// +// Definitions of supported Corollary C-bus platforms +// +#define COROLLARY_CBUS1 1 // Corollary C-bus 1 system +#define COROLLARY_CBUS2 2 // Corollary C-bus 2 system + +// +// Definitions of the switch table for multiple Corollary C-bus platforms. +// The structure of this is defined by the layout of the Windows NT HAL. +// The first three entries are captured in variables as well (for speed) +// since they are dereferenced frequently throughout the life of the system. +// +// WARNING! This structure must match the one in cbus.inc !!! +// +typedef struct _CBUS_NTHAL { + + VOID (*HalRequestInterrupt)( IN ULONG); + + VOID (*HalRequestSoftwareInterrupt)( IN KIRQL); + + LARGE_INTEGER (*HalQueryPerformanceCounter)(IN OUT PLARGE_INTEGER); + + VOID (*BootCPU)( IN ULONG processor, IN ULONG); + + + VOID (*InitializePlatform)(VOID); + + VOID (*InitializeCPU)(ULONG); + + BOOLEAN (*EnableNonDeviceInterrupt)(ULONG); + + VOID (*EnableDeviceInterrupt)(ULONG, PVOID, ULONG, USHORT, USHORT); + + + VOID (*DisableInterrupt)(ULONG, PVOID, ULONG, USHORT, USHORT); + + PVOID (*LinkVector)(PBUS_HANDLER, ULONG, ULONG); + + ULONG (*MapVector)(PBUS_HANDLER, ULONG, ULONG, PKIRQL); + + VOID (*ParseRRD)(IN OUT PEXT_ID_INFO, IN OUT PULONG); + + + NTSTATUS (*ResolveNMI)(IN PVOID); + + VOID (*HalInitializeInterrupts)(IN ULONG); + + VOID (*ResetAllOtherProcessors)(IN ULONG); + + VOID (*InitOtherBuses)(VOID); + + + ULONG (*HalSetTimeIncrement)(IN ULONG); + + VOID (*CheckBusRanges)(VOID); + + VOID (*AddMemoryHoles)(VOID); + + VOID (*InitializeOtherPciBus)(VOID); + +} CBUS_NTHAL, *PCBUS_NTHAL; + +extern PCBUS_NTHAL CbusBackend; + +// +// software categories for each possible I/O interrupt +// + +#define SW_BINDTOBOOT 0x0 +#define SW_LIG 0x1 +#define SW_GOTOALL 0x2 +#define SW_DISABLED 0x3 + +// +// software categories for interrupt vectors when detaching +// + +#define LAST_CPU_DETACH 0x0 +#define NOT_LAST_CPU_DETACH 0x1 + +#endif // _CBUS_NT_H diff --git a/private/ntos/nthals/halcbus/i386/cbus_sw.c b/private/ntos/nthals/halcbus/i386/cbus_sw.c new file mode 100644 index 000000000..bd674c11a --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbus_sw.c @@ -0,0 +1,191 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus_sw.c + +Abstract: + + This module defines the switch table for various C-bus platform + architectures under Windows NT. During initialization, the + Corollary HAL will revector all hardware-specific actions + through the switch table declared here. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + + +--*/ + +#include "halp.h" +#include "cbus_nt.h" // C-bus NT-specific implementation stuff +#include "cbusrrd.h" // HAL <-> RRD interface definitions + +BOOLEAN +CbusMPMachine(VOID); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, CbusMPMachine) +#endif + +extern VOID Cbus2RequestIpi ( IN ULONG Mask); +extern VOID Cbus2RequestSoftwareInterrupt (KIRQL); +extern VOID Cbus2BootCPU (ULONG, ULONG); +extern VOID Cbus2InitializePlatform ( VOID); +extern VOID Cbus2InitializeCPU ( ULONG); +extern BOOLEAN Cbus2EnableNonDeviceInterrupt(ULONG); +extern VOID Cbus2EnableDeviceInterrupt(ULONG, PVOID, ULONG, USHORT, USHORT); +extern VOID Cbus2DisableInterrupt(ULONG, PVOID, ULONG, USHORT, USHORT); +extern PVOID Cbus2LinkVector(PBUS_HANDLER, ULONG, ULONG); +extern ULONG Cbus2MapVector(PBUS_HANDLER, ULONG, ULONG, PKIRQL); +extern VOID Cbus2ParseRRD(IN PVOID, IN OUT PULONG); +extern NTSTATUS Cbus2ResolveNMI(PVOID); +extern VOID Cbus2InitializeInterrupts(ULONG); +extern LARGE_INTEGER Cbus2QueryPerformanceCounter(IN OUT PLARGE_INTEGER); +extern VOID Cbus2ResetAllOtherProcessors(ULONG); +extern VOID Cbus2InitOtherBuses(VOID); +extern ULONG Cbus2SetTimeIncrement(ULONG); +extern VOID Cbus2CheckBusRanges(VOID); +extern VOID Cbus2AddMemoryHoles(VOID); +extern VOID Cbus2InitializeOtherPciBus(VOID); + +extern VOID CbusRequestApicIpi ( IN ULONG Mask); +extern VOID CbusRequestApicSoftwareInterrupt ( IN KIRQL Rirql); +extern VOID Cbus1BootCPU ( IN ULONG, IN ULONG); +extern VOID Cbus1InitializePlatform ( VOID); +extern VOID Cbus1InitializeCPU ( ULONG); +extern BOOLEAN Cbus1EnableNonDeviceInterrupt(IN ULONG); +extern VOID Cbus1EnableDeviceInterrupt(ULONG, PVOID, ULONG, USHORT, USHORT); +extern VOID Cbus1DisableInterrupt(ULONG, PVOID, ULONG, USHORT, USHORT); + + +extern VOID Cbus1ParseRRD(IN PVOID, IN OUT PULONG); + +extern PVOID Cbus1LinkVector(PBUS_HANDLER, ULONG, ULONG); + +extern ULONG Cbus1MapVector(PBUS_HANDLER, ULONG, ULONG, PKIRQL); + +extern NTSTATUS Cbus1ResolveNMI(PVOID); + +extern VOID Cbus1InitializeInterrupts(ULONG); +extern LARGE_INTEGER Cbus1QueryPerformanceCounter(PLARGE_INTEGER); +extern VOID Cbus1ResetAllOtherProcessors(ULONG); +extern ULONG Cbus1SetTimeIncrement(ULONG); + +typedef VOID (*VOID_FUNCTION)(VOID); + +CBUS_NTHAL cbus2_nthal = { + Cbus2RequestIpi, + Cbus2RequestSoftwareInterrupt, + Cbus2QueryPerformanceCounter, + Cbus2BootCPU, + + Cbus2InitializePlatform, + Cbus2InitializeCPU, + Cbus2EnableNonDeviceInterrupt, + Cbus2EnableDeviceInterrupt, + + Cbus2DisableInterrupt, + Cbus2LinkVector, + Cbus2MapVector, + Cbus2ParseRRD, + + Cbus2ResolveNMI, + Cbus2InitializeInterrupts, + Cbus2ResetAllOtherProcessors, + Cbus2InitOtherBuses, + + Cbus2SetTimeIncrement, + Cbus2CheckBusRanges, + Cbus2AddMemoryHoles, + Cbus2InitializeOtherPciBus, +}; + +CBUS_NTHAL cbus1_nthal = { + CbusRequestApicIpi, + CbusRequestApicSoftwareInterrupt, + Cbus1QueryPerformanceCounter, + Cbus1BootCPU, + + Cbus1InitializePlatform, + Cbus1InitializeCPU, + Cbus1EnableNonDeviceInterrupt, + Cbus1EnableDeviceInterrupt, + + Cbus1DisableInterrupt, + Cbus1LinkVector, + Cbus1MapVector, + Cbus1ParseRRD, + + Cbus1ResolveNMI, + Cbus1InitializeInterrupts, + Cbus1ResetAllOtherProcessors, + (VOID_FUNCTION)0, + + Cbus1SetTimeIncrement, + (VOID_FUNCTION)0, + (VOID_FUNCTION)0, + (VOID_FUNCTION)0 +}; + +PCBUS_NTHAL CbusBackend; + +BOOLEAN +CbusMPMachine(VOID) +/*++ + + Routine Description: + + recognize which type of C-bus multiprocessor system it is: + + C-bus I Symmetric XM + C-bus II: + + Use CbusGlobal to determine which type of machine this is. + note the ordering of the platform recognition is important for future + expansion. + +Arguments: + + None. + +Return Value: + + TRUE if this Corollary MP machine is supported by this MP HAL. + FALSE if not. + +--*/ +{ + ULONG machine; + + machine = CbusGlobal.machine_type; + + if (machine & MACHINE_CBUS2) { + + if ((CbusGlobal.supported_environments & WINDOWS_NT_R2) == 0) + return FALSE; + + CbusBackend = &cbus2_nthal; + return TRUE; + } + + if (machine & MACHINE_CBUS1_XM) { + + if ((CbusGlobal.supported_environments & (WINDOWS_NT|WINDOWS_NT_R2)) == 0) + return FALSE; + + CbusBackend = &cbus1_nthal; + return TRUE; + } + + return FALSE; +} diff --git a/private/ntos/nthals/halcbus/i386/cbusapic.asm b/private/ntos/nthals/halcbus/i386/cbusapic.asm new file mode 100644 index 000000000..c71a43b68 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusapic.asm @@ -0,0 +1,715 @@ + title "Software Interrupts" +;++ +; +;Copyright (c) 1992, 1993, 1994 Corollary Inc +; +;Module Name: +; +; cbusapic.asm +; +;Abstract: +; +; This module implements the low-level Corollary Cbus HAL routines to deal +; with the Intel APIC distributed interrupt controller. +; +; This includes the_sending_ of software and IPI interrupts in Windows NT. +; The receipt of these interrupts is handled elsewhere. +; +; Note: The routines in this module are jmp'ed to directly from +; their common Hal counterparts. +; +;Author: +; +; Landy Wang (landy@corollary.com) 26-Mar-1992 +; +;Environment: +; +; Kernel Mode +; +;Revision History: +; +;-- + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include cbus.inc + + EXTRNP _CbusApicRedirectionInterrupt,0 + +; +; APIC register offsets... +; +APIC_IRR_OFFSET equ 0100h ; offset of APIC IRR registers +APIC_APC_IRR equ 0103h ; offset of APIC APC IRR register +APIC_DPC_IRR equ 0105h ; offset of APIC DPC IRR register +APIC_ICR_OFFSET equ 0300h ; offset of APIC intr cmd register +APIC_ICR_DEST_OFFSET equ 0310h ; offset of APIC intr cmd dest register + +; +; APIC register bitfield definitions... +; +APIC_DEASSERT_RESET equ 000500h ; sending a DEASSERT-RESET command +APIC_LOGICAL_MODE equ 000800h ; sending an APIC-LOGICAL interrupt +APIC_ICR_BUSY equ 001000h ; APIC intr command reg is busy +APIC_TRIGGER_LEVEL equ 008000h ; generate a level interrupt +APIC_INTR_DISABLED equ 010000h ; disable this redirection entry +APIC_SELFINTR equ 040000h ; APIC's self-interrupt code +APIC_ALLINCLSELF equ 080000h ; sending a DEASSERT-RESET command + +APIC_FULL_DRESET equ (APIC_ALLINCLSELF or APIC_TRIGGER_LEVEL or APIC_LOGICAL_MODE or APIC_DEASSERT_RESET) + +; +; the IOAPIC_REGISTERS_T register access template... +; +RegisterSelect equ 0h ; this APIC's register select +WindowRegister equ 010h ; this APIC's window register + +; +; left shift needed to convert processor_bit to Intel APIC ID - this applies +; to the logical destination ID and redirection entry registers only. +; +APIC_BIT_TO_ID equ 24 ; also in cbus1.h + +; +; macro to wait for the delivery status register to become idle +; +APIC_WAIT macro apicreg + local a + + align 4 +a: + test dword ptr [apicreg + APIC_ICR_OFFSET], APIC_ICR_BUSY + jnz short a + +endm + +IOAPIC_READ macro ioapic, offset, answer + ; + ; 'ioapic' must point at the I/O APIC + ; 'offset' is the offset to peek + ; 'answer' is the peeked return value + ; + mov dword ptr RegisterSelect[ioapic], offset + + mov answer, dword ptr WindowRegister[ioapic] +endm + + +IOAPIC_WRITE macro ioapic, offset, value + ; + ; 'ioapic' must point at the I/O APIC + ; 'offset' is the offset to poke + ; 'value' is the value to poke + ; + mov dword ptr RegisterSelect[ioapic], offset + + mov dword ptr WindowRegister[ioapic], value +endm + + .list + +INIT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; CbusApicArbsync ( VOID ) +; +; Routine Description: +; +; Broadcast an ALL-INCLUDING-SELF interrupt with deassert, reset & +; physical mode set. This routine is called after each APIC assigns +; itself a unique ID that can be used in APIC bus arbitration and +; priority arbitration. This syncs up the picture that each APIC +; has with the new ID that has just been added. +; +; Arguments: +; +; None. +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _CbusApicArbsync ,0 + + ; get the base of APIC space, so we can then access + ; the addr of hardware interrupt command register below + + mov ecx, [_CbusLocalApic] + + ; + ; disable interrupts so that polling the register and + ; poking it becomes an atomic operation (for this processor), + ; as specified in the Intel 82489DX specification. + ; this is needed since interrupt service routines must + ; be allowed to send IPIs (for example, DPCs, etc). + ; + + pushfd + cli + + ; wait for the delivery status register to become idle + + APIC_WAIT ecx + + ; + ; it is ILLEGAL to use the "destination shorthand" mode of the APIC + ; for this command - we must set up the whole 64 bit register). + ; both destination and vector are DONT_CARE for this request. + ; + ; no recipients (probably a don't care), but must be written + ; _before_ the command is sent... + + mov dword ptr [ecx + APIC_ICR_DEST_OFFSET], 0 + + ; + ; now we can send the full deassert-reset command + ; + + mov dword ptr [ecx + APIC_ICR_OFFSET], APIC_FULL_DRESET + + popfd + + stdRET _CbusApicArbsync +stdENDP _CbusApicArbsync + +INIT ENDS + +_TEXT SEGMENT DWORD PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; CbusRequestApicSoftwareInterrupt ( +; IN KIRQL RequestIrql +; ) +; +; Routine Description: +; +; This routine is used to issue a software interrupt to the +; calling processor. Since this is all done in hardware, the +; code to implement this is trivial. Our hardware supports +; sending the interrupt to lowest-in-group processors, which +; would be useful for a good number of DPCs, for example, but +; the kernel doesn't currently tell us which kinds of software +; interrupts need to go to the caller versus which can go to +; any processor. +; +; Arguments: +; +; (esp+4) = RequestIrql - Supplies the request IRQL value +; +; Return Value: +; +; None. +; +;-- + +; +; equates for accessing arguments +; + +KsiRequestIrql equ byte ptr [esp+4] +; + +cPublicProc _CbusRequestApicSoftwareInterrupt ,1 + + xor ecx, ecx ; faster than movzx + mov cl, KsiRequestIrql ; to get irql + + ; + ; disable interrupts so that polling the register and + ; poking it becomes an atomic operation (for this processor), + ; as specified in the Intel 82489DX specification. + ; this is needed since interrupt service routines must + ; be allowed to send IPIs (for example, DPCs, etc). + ; + + pushfd + cli + + ; + ; notice the CbusIrqlToVector[] indexing below -- it means + ; that only pre-defined software interrupts can be sent, + ; NOT any "int xx" command on demand. + ; + mov eax, [_CbusIrqlToVector+4*ecx] ; get vector to issue + + ; + ; if this function is ever changed so that we are + ; allowed to interrupt a different processor than + ; the caller, we will not be able to use the APIC + ; shorthand method below to address them, and we will + ; have to change the selfintr mode we use to issue. + ; HalRequestApicIpi() already does these types of things, btw. + ; + + or eax, APIC_SELFINTR + + ; get the base of APIC space, so we can then access + ; the addr of hardware interrupt command register below + + mov ecx, [_CbusLocalApic] + + ; wait for the delivery status register to become idle + + APIC_WAIT ecx + + ; + ; since we are just interrupting ourself, we can use + ; the "destination shorthand" mode of the APIC and + ; just set up the single 32-bit write, instead of doing + ; the whole 64 bit register). So, + ; + ; the APIC icr.destination = DONT_CARE + ; the APIC icr.vector = IPI_TASKPRI + ; the APIC icr.destination_shorthand = 01 (SELF); + ; + + mov [ecx + APIC_ICR_OFFSET], eax + + ; + ; The interrupt must be pending before returning + ; wait for the delivery status register to become idle. + ; the delivery status register being idle just means that + ; this local APIC has sent the interrupt message out on the + ; APIC bus (it has to do this even for self interrupts!). + ; + ; but waiting for delivery status to be idle is NOT ENOUGH !!! + ; you must also wait for the IRR bit to be set. this means + ; this APIC's local unit has accepted the interrupt and the + ; CPU has not yet sent the APIC an EOI. + ; + + APIC_WAIT ecx + + popfd + + stdRET _CbusRequestApicSoftwareInterrupt + +stdENDP _CbusRequestApicSoftwareInterrupt + + + page ,132 + subttl "CbusRequestApicIpi" +;++ +; +; VOID +; CbusRequestApicIpi( +; IN ULONG Mask +; ); +; +; Routine Description: +; +; Requests an interprocessor interrupt +; +; for Windows NT, we use full distributed +; interrupt capability, and, thus, we will IGNORE the sswi address +; that RRD passes us and prioritize IPI as we see fit, given the +; other devices configured into the system. +; +; Arguments: +; +; Mask - Mask of processors to be interrupted +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _CbusRequestApicIpi ,1 + mov eax, [_CbusIpiVector] + + mov edx, [esp+4] ; get requested recipients + + ; + ; translate logical processor mask into the high byte of edx, + ; since this is the only portion of the logical destination register + ; that future APICs will use to compare with. + ; + shl edx, APIC_BIT_TO_ID + + ; get the base of APIC space, so we can then access + ; the addr of hardware interrupt command register below + + mov ecx, [_CbusLocalApic] + + ; + ; disable interrupts so that polling the register and + ; poking it becomes an atomic operation (for this processor), + ; as specified in the Intel 82489DX specification. + ; this is needed since interrupt service routines must + ; be allowed to send IPIs (for example, DPCs, etc). + ; + + pushfd + cli + + ; wait for the delivery status register to become idle + + APIC_WAIT ecx + + ; use APIC logical mode to pop randomly-specified sets of processors + ; we cannot use "destination shorthand" mode for this; + ; we must write the whole 64 bit register. ie: + ; + ; The APIC icr.destination = processor_mask + ; The APIC icr.vector = IPI_TASKPRI + ; The APIC icr.destination_mode = 1 (LOGICAL); + ; + ; all other fields are zero. + ; + ; note that the high 32 bits of the interrupt command + ; register must be written _BEFORE_ the low 32 bits. + ; + + ; specify the CPUs... + mov [ecx + APIC_ICR_DEST_OFFSET], edx + + ; send the command... + or eax, APIC_LOGICAL_MODE ; set up mode & vector + mov dword ptr [ecx + APIC_ICR_OFFSET], eax + + popfd + + stdRET _CbusRequestApicIpi +stdENDP _CbusRequestApicIpi + + +;++ +; +; ULONG +; READ_IOAPIC_ULONG( +; ULONG ApicNumber, +; PULONG Port +; ) +; +; Routine Description: +; +; Read the specified offset of the specified I/O APIC. +; +; +; Arguments: +; (esp+4) = Logical Apic Number +; (esp+8) = Port +; +; Returns: +; Value in Port. +; +;-- +cPublicProc _READ_IOAPIC_ULONG ,2 + + mov ecx, [esp + 4] ; Apic number to access + mov eax, [_CbusIOApic+4*ecx] ; point at the I/O APIC + mov edx, [esp + 8] ; offset to peek + + IOAPIC_READ eax, edx, eax + + stdRET _READ_IOAPIC_ULONG +stdENDP _READ_IOAPIC_ULONG + + +;++ +; +; VOID +; WRITE_IOAPIC_ULONG( +; ULONG ApicNumber, +; PULONG Port, +; ULONG Value +; ) +; +; Routine Description: +; +; Write the specified offset with the specified value into +; the calling processor's I/O APIC. +; +; Arguments: +; (esp+4) = Logical Apic Number +; (esp+8) = Port +; (esp+c) = Value +; +;-- +cPublicProc _WRITE_IOAPIC_ULONG ,3 + + mov ecx, [esp + 4] ; Apic number to access + mov eax, [_CbusIOApic+4*ecx] ; point at the I/O APIC + mov edx, [esp + 8] ; offset to poke + mov ecx, [esp + 0ch] ; value for the poke + + IOAPIC_WRITE eax, edx, ecx + + stdRET _WRITE_IOAPIC_ULONG +stdENDP _WRITE_IOAPIC_ULONG + + + page ,132 + subttl "I/O APIC Update Interrupt" +;++ +; +; VOID +; IOApicUpdate( +; VOID +; ); +; +; Routine Description: +; +; This routine is the interrupt handler for an IPI interrupt generated +; at a priority just below that of normal IPIs. Its function is to +; poke the I/O APIC with the new masks that have been requested so +; that an interrupt can be accepted or ignored on a given processor. +; +; The priorities of this and CbusAllocateVector() have been carefully +; chosen so as to avoid deadlock. +; +; This routine is needed because each I/O APIC is only addressable from +; its local CPU. +; +; since this routine is entered directly via an interrupt gate, interrupt +; protection via cli is not necessary. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST hiui_a, hiui_t + +cPublicProc _IOApicUpdate ,0 + + ; + ; Save machine state on trap frame + ; + + ENTER_INTERRUPT hiui_a, hiui_t + + ; keep it simple, just issue the EOI right now. + ; no changing of taskpri/irql is needed here. + ; Thus, the EOI serves as the HalEndSystemInterrupt. + + mov eax, _CbusRedirVector + CBUS_EOI eax, ecx ; destroy eax & ecx + + stdCall _CbusApicRedirectionInterrupt + + ; + ; Call this directly instead of through INTERRUPT_EXIT + ; because the HalEndSystemInterrupt has already been done, + ; and must only be done ONCE per interrupt. + ; + + cli + SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi + +stdENDP _IOApicUpdate + + + page ,132 + subttl "CbusApicRedirectionRequest" +;++ +; +; VOID +; CbusApicRedirectionRequest(IN OUT PULONG spinaddress) +; ); +; +; Routine Description: +; +; Requests an interprocessor interrupt, at the HAL private CBUS1_REDIR_IPI +; priority. this must be higher than any device priority to prevent +; deadlocks. this routine always interrupts the processor that can +; access the I/O unit of the APIC distributing the interrupts amongst +; all the processors. we have currently wired that up to the boot +; processor for Cbus1. +; +; the boot processor will receive the interrupt in the IOApicUpdate() +; routine above. +; +; Arguments: +; +; spinaddress - the caller will spin until the dword pointed to by +; this variable becomes zero. this insures that the +; processor controlling the APIC has actually satisfied +; our request. +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _CbusApicRedirectionRequest ,1 + + ; + ; set the vector we're going to send + ; + mov edx, [_CbusRedirVector] + + ; get the base of APIC space, so we can then access + ; the addr of hardware interrupt command register below + + mov ecx, [_CbusLocalApic] + + ; create the boot CPU ( 1 << 24) APIC ID in a portable manner... + + mov eax, 1 + shl eax, APIC_BIT_TO_ID + + ; + ; disable interrupts so that polling the register and + ; poking it becomes an atomic operation (for this processor), + ; as specified in the Intel 82489DX specification. + ; this is needed since interrupt service routines must + ; be allowed to send IPIs (for example, DPCs, etc). + ; + + pushfd + cli + + ; wait for the delivery status register to become idle + + APIC_WAIT ecx + + ; can't use "destination shorthand" mode for this - + ; must write the whole 64 bit register. ie: + ; + ; The APIC icr.destination = processor_mask + ; The APIC icr.vector = IPI_TASKPRI + ; The APIC icr.destination_mode = 1 (LOGICAL); + ; + ; all other fields are zero. + ; + ; note that the destination word of the interrupt command + ; register must be written _BEFORE_ the command word. + ; + + mov [ecx + APIC_ICR_DEST_OFFSET], eax + + or edx, APIC_LOGICAL_MODE ; set up mode & vector + + ; send the command... + mov dword ptr [ecx + APIC_ICR_OFFSET], edx + + ; now wait for the processor controlling the APIC to finish our request + ; do this in assembly so the compiler won't optimize this incorrectly + + mov eax, [esp+8] ; remember we pushed flags + + align 4 +@@: + cmp dword ptr [eax], 0 + jne @b + + popfd + + stdRET _CbusApicRedirectionRequest +stdENDP _CbusApicRedirectionRequest + + page ,132 + subttl "Cbus1RebootRequest" +;++ +; +; VOID +; Cbus1RebootRequest(IN ULONG Processor); +; +; Routine Description: +; +; +; Arguments: +; +; Requests an interprocessor interrupt, at the HAL private CbusRebootVector. +; This interrupt is always sent to the non-boot processors. +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _Cbus1RebootRequest ,1 + mov edx, 1 + mov ecx, [_CbusProcessors] ; set mask for all processors + shl edx, cl + sub edx, 1 + + mov eax, 1 + mov ecx, [esp+8] ; get the requesting processor + shl eax, cl + not eax + and edx, eax + + mov eax, [_CbusRebootVector] + + cmp edx, 0 ; check for uniprocessor case + je no_ipi_needed + + ; + ; translate logical processor mask into the high byte of edx, + ; since this is the only portion of the logical destination register + ; that future APICs will use to compare with. + ; + shl edx, APIC_BIT_TO_ID + + ; get the base of APIC space, so we can then access + ; the addr of hardware interrupt command register below + + mov ecx, [_CbusLocalApic] + + ; + ; disable interrupts so that polling the register and + ; poking it becomes an atomic operation (for this processor), + ; as specified in the Intel 82489DX specification. + ; this is needed since interrupt service routines must + ; be allowed to send IPIs (for example, DPCs, etc). + ; + + pushfd + cli + + ; wait for the delivery status register to become idle + + APIC_WAIT ecx + + ; use APIC logical mode to pop randomly-specified sets of processors + ; we cannot use "destination shorthand" mode for this; + ; we must write the whole 64 bit register. ie: + ; + ; The APIC icr.destination = processor_mask + ; The APIC icr.vector = IPI_TASKPRI + ; The APIC icr.destination_mode = 1 (LOGICAL); + ; + ; all other fields are zero. + ; + ; note that the high 32 bits of the interrupt command + ; register must be written _BEFORE_ the low 32 bits. + ; + + ; specify the CPUs... + mov [ecx + APIC_ICR_DEST_OFFSET], edx + + ; send the command... + or eax, APIC_LOGICAL_MODE ; set up mode & vector + mov dword ptr [ecx + APIC_ICR_OFFSET], eax + + popfd + +no_ipi_needed: + + stdRET _Cbus1RebootRequest +stdENDP _Cbus1RebootRequest + +_TEXT ENDS + END diff --git a/private/ntos/nthals/halcbus/i386/cbusapic.h b/private/ntos/nthals/halcbus/i386/cbusapic.h new file mode 100644 index 000000000..ad3677e10 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusapic.h @@ -0,0 +1,256 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbusapic.h + +Abstract: + + Cbus APIC architecture definitions for the Corollary Cbus1 + and Cbus2 multiprocessor HAL modules. + +Author: + + Landy Wang (landy@corollary.com) 05-Oct-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#ifndef _CBUSAPIC_ +#define _CBUSAPIC_ + +// +// +// THE APIC HARDWARE ARCHITECTURE DEFINITIONS +// +// + +#define LOCAL_APIC_ENABLE 0x100 // all APIC intrs now enabled + +#define APIC_INTR_UNMASKED 0x0 // this interrupt now enabled +#define APIC_INTR_MASKED 0x1 // this interrupt now disabled + +#define APIC_INTR_FIXED 0x0 // this interrupt is tied to a CPU +#define APIC_INTR_LIG 0x1 // this interrupt is lowest-in-group +#define APIC_INTR_NMI 0x4 // this interrupt is an NMI + +#define APIC_EDGE 0x0 // this interrupt is edge-triggered +#define APIC_LEVEL 0x1 // this interrupt is level-triggered +#define APIC_LOGICAL_MODE 0x1 // only logical mode is being used + +#define APIC_ALL_PROCESSORS (ULONG)-1 // Destination format reg default + +// +// left shift needed to convert processor_bit to Intel APIC ID. +// this applies to the: +// +// I/O unit ID register +// I/O unit ID redirection entry destination registers +// local unit ID register +// local unit logical destination register +// + +#define APIC_BIT_TO_ID 24 // also in cbusapic.asm + +// +// both LOCAL and I/O APICs must be on page-aligned boundaries +// for the current HalpMapPhysicalMemory() calls to work. +// + +// +// The physical address of the local and I/O APICs are architecture +// dependent, and therefore reside in cbus1.h & cbus2.h. The size +// of the APIC is not architecture dependent, and thus is declared here. +// + +#define LOCAL_APIC_SIZE 0x400 +#define IO_APIC_SIZE 0x11 + +// +// I/O APIC registers. note only register select & window register are +// directly accessible in the address space. other I/O registers are +// reached via these two registers, similar to the CMOS access method. +// +#define IO_APIC_REGSEL 0x00 +#define IO_APIC_WINREG 0x10 + +#define IO_APIC_ID_OFFSET 0x00 +#define IO_APIC_VERSION 0x01 +#define IO_APIC_REDIRLO 0x10 +#define IO_APIC_REDIRHI 0x11 + +// +// Each I/O APIC has one redirection entry for each interrupt it handles. +// note that since it is not directly accessible (instead the window register +// must be used), consecutive entries are byte aligned, not 16 byte aligned. +// +typedef union _redirection_t { + struct { + ULONG Vector : 8; + ULONG Delivery_mode : 3; + ULONG Dest_mode : 1; + ULONG Delivery_status : 1; // read-only + ULONG Reserved0 : 1; + ULONG Remote_irr : 1; + ULONG Trigger : 1; + ULONG Mask : 1; + ULONG Reserved1 : 15; + ULONG Destination; + } ra; + struct { + ULONG dword1; + ULONG dword2; + } rb; +} REDIRECTION_T; + +// +// The Interrupt Command register format is used for IPIs, and is +// here purely for reference. It is actually only used by cbusapic.asm, +// which has its internal (identical) conception of this register. +// No C code references this structure. +// +typedef struct _intrcommand_t { + ULONG vector : 8; + ULONG delivery_mode : 3; + ULONG dest_mode : 1; + ULONG delivery_status : 1; // read-only + ULONG reserved0 : 1; + ULONG level : 1; + ULONG trigger : 1; + ULONG remote_read_status : 2; + ULONG destination_shorthand : 2; + ULONG reserved1 : 12; + ULONG pad[3]; + ULONG destination; +} INTRCOMMAND_T, *PINTRCOMMAND; + +typedef struct _apic_registers_t { + UCHAR fill0[0x20]; + + ULONG LocalUnitID; // 0x20 + UCHAR fill1[0x80 - 0x20 - sizeof(ULONG)]; + + TASKPRI_T ApicTaskPriority; // 0x80 + UCHAR fill2[0xB0 - 0x80 - sizeof(TASKPRI_T)]; + + ULONG ApicEOI; // 0xB0 + UCHAR fill3[0xD0 - 0xB0 - sizeof(ULONG)]; + + ULONG ApicLogicalDestination; // 0xD0 + UCHAR fill4[0xE0 - 0xD0 - sizeof(ULONG)]; + + ULONG ApicDestinationFormat; // 0xE0 + UCHAR fill5[0xF0 - 0xE0 - sizeof(ULONG)]; + + ULONG ApicSpuriousVector; // 0xF0 + UCHAR fill6[0x300 - 0xF0 - sizeof(ULONG)]; + + INTRCOMMAND_T ApicICR; // 0x300 + UCHAR fill7[0x360 - 0x300 - sizeof(INTRCOMMAND_T)]; + + REDIRECTION_T ApicLocalInt1; // 0x360 + UCHAR fill8[0x4D0 - 0x360 - sizeof(REDIRECTION_T)]; + + // + // Note that APMode is also the PolarityPortLow register + // + UCHAR APMode; // 0x4D0 + UCHAR PolarityPortHigh; // 0x4D1 + +} APIC_REGISTERS_T, *PAPIC_REGISTERS; + +#define LOWEST_APIC_PRI 0x00 +#define LOWEST_APIC_DEVICE_PRI 0x40 +#define HIGHEST_APIC_PRI 0xF0 + +// +// +// PURE SOFTWARE DEFINITIONS HERE +// +// + +// +// this structure is used for communications between processors +// when modifying the I/O APIC. +// +typedef struct _redirport_t { + ULONG Status; + ULONG ApicID; + ULONG BusNumber; + ULONG RedirectionAddress; + ULONG RedirectionCommand; + ULONG RedirectionDestination; +} REDIR_PORT_T, *PREDIR_PORT_T; + +#define REDIR_ACTIVE_REQUEST 0x01 +#define REDIR_ENABLE_REQUEST 0x02 +#define REDIR_DISABLE_REQUEST 0x04 +#define REDIR_LASTDETACH_REQUEST 0x08 +#define REDIR_FIRSTATTACH_REQUEST 0x10 + + +// +// declare APIC-related external functions +// + + +VOID +CbusInitializeLocalApic( +IN ULONG Processor, +IN PVOID PhysicalApicLocation, +IN ULONG SpuriousVector +); + + +VOID +CbusInitializeIOApic( +IN ULONG Processor, +IN PVOID PhysicalApicLocation, +IN ULONG RedirVector, +IN ULONG RebootVector, +IN ULONG IrqPolarity +); + +VOID +CbusEnableApicInterrupt( +IN ULONG ApicBusNumber, +IN ULONG Vector, +IN PVOID HardwarePtr, +IN ULONG FirstAttach, +IN BOOLEAN LowestInGroup, +IN BOOLEAN LevelTriggered +); + +VOID +CbusDisableApicInterrupt( +IN ULONG ApicBusNumber, +IN ULONG Vector, +IN PVOID HardwarePtr, +IN ULONG LastDetach +); + +PVOID +CbusApicLinkVector( +IN PBUS_HANDLER Bus, +IN ULONG Vector, +IN ULONG Irqline +); + +VOID +CbusApicBrandIOUnitID( +IN ULONG Processor +); + +// +// end of APIC-related external function declarations +// + + +#endif // _CBUSAPIC_ diff --git a/private/ntos/nthals/halcbus/i386/cbusboot.asm b/private/ntos/nthals/halcbus/i386/cbusboot.asm new file mode 100644 index 000000000..3e0770eef --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusboot.asm @@ -0,0 +1,362 @@ + title "MP primitives for the Corollary Cbus machines" +;++ +; +;Copyright (c) 1991 Microsoft Corporation +; +;Module Name: +; +; cbusboot.asm +; +;Abstract: +; +; Corollary Start Next Processor assembly code +; +; This module along implements the code to start +; off the additional processors in the Corollary machines. +; +; +;Author: +; +; Ken Reneris (kenr) 12-Jan-1992 +; +;Environment: +; Kernel mode. +; +; +;Revision History: +; +; Landy Wang (landy@corollary.com) 20-Oct-1992 +; +; - slight modification to boot Corollary-architecture processors +; to allow booting multiple hardware platforms +; +; - preserve interrupt state in eflags whilst booting other processors +; +;-- + + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include cbus.inc + + .list + +WarmResetVector equ 467h ; warm reset vector in ROM data segment + + +; +; Internal defines and structures +; + +PxParamBlock struc + SPx_flag dd ? + SPx_TiledCR3 dd ? + SPx_P0EBP dd ? + SPx_PB db ProcessorStateLength dup (?) +PxParamBlock ends + + +_TEXT SEGMENT PARA PUBLIC 'CODE' ; Start 32 bit code + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; BOOLEAN +; HalStartNextProcessor ( +; IN PLOADER_BLOCK pLoaderBlock, +; IN PKPROCESSOR_STATE pProcessorState +; ) +; +; Routine Description: +; +; This routine is called by the kernel durning kernel initialization +; to obtain more processors. It is called until no more processors +; are available. +; +; If another processor exists this function is to initialize it to +; the passed in processorstate structure, and return TRUE. +; +; If another processor does not exists, then a FALSE is returned. +; +; Also note that the loader block has been setup for the next processor. +; The new processor logical thread number can be obtained from it, if +; required. +; +; Arguments: +; pLoaderBlock, - Loader block which has been intialized for the +; next processor. +; +; pProcessorState - The processor state which is to be loaded into +; the next processor. +; +; +; Return Value: +; +; TRUE - ProcessorNumber was dispatched. +; FALSE - A processor was not dispatched. no other processors exists. +; +;-- + +pLoaderBlock equ dword ptr [ebp+8] ; zero based +pProcessorState equ dword ptr [ebp+12] + +; +; Local variables +; + +PxFrame equ [ebp - size PxParamBlock] + + +cPublicProc _HalStartNextProcessor ,2 + push ebp ; save ebp + mov ebp, esp ; + + sub esp, size PxParamBlock ; Make room for local vars + + + push esi + push edi + push ebx + + xor eax, eax + mov PxFrame.SPx_flag, eax + + cmp _MpCount, eax + jbe snp_exit ; exit FALSE + + mov esi, OFFSET FLAT:StartPx_RMStub + mov ecx, StartPx_RMStub_Len + mov edi, _MpLowStub ; Copy RMStub to low memory + add edi, size PxParamBlock + rep movsb + + lea edi, PxFrame.SPx_PB + mov esi, pProcessorState + mov ecx, ProcessorStateLength ; Copy processorstate + rep movsb ; to PxFrame + + stdCall _HalpBuildTiledCR3, <pProcessorState> + + mov PxFrame.SPx_TiledCR3, eax + mov PxFrame.SPx_P0EBP, ebp + + mov eax, pLoaderBlock ; lookup processor # we are + mov eax, [eax].LpbPrcb ; starting + movzx eax, byte ptr [eax].PbNumber + + mov ecx, size PxParamBlock ; copy param block + lea esi, PxFrame ; to low memory stub + mov edi, _MpLowStub + mov eax, edi + rep movsb + + add eax, size PxParamBlock + mov ebx, OFFSET FLAT:StartPx_RMStub + sub eax, ebx ; (eax) = adjusted pointer + mov bx, word ptr [PxFrame.SPx_PB.PsContextFrame.CsSegCs] + mov [eax.SPrxFlatCS], bx ; patch realmode stub with + mov [eax.SPrxPMStub], offset _StartPx_PMStub ; valid long jump + + mov ebx, _MppIDT + add ebx, WarmResetVector + + pushfd ; must preserve flags + cli + push dword ptr [ebx] ; Save current vector + + mov edx, _MpLowStubPhysicalAddress + shl edx, 12 ; seg:0 + add edx, size PxParamBlock + mov dword ptr [ebx], edx ; start Px here + + mov ecx, pLoaderBlock ; lookup processor # we are + mov ecx, [ecx].LpbPrcb ; starting + movzx ecx, byte ptr [ecx].PbNumber + + push edx ; start address (seg:off) + push ecx ; processor number + + mov eax, _CbusBackend ; use hardware handler + call HalBootCPU[ eax ] ; to boot specified processor + ; callee pops the args + + align 4 +Hsnp002: +@@: + cmp PxFrame.SPx_flag, 0 ; wait for Px to get its + jz @b ; info + + pop dword ptr [ebx] ; restore WarmResetVector + popfd ; must restore flags + + stdCall _HalpFreeTiledCR3 ; free memory used for tiled + ; CR3 + + dec _MpCount ; one less + mov eax, 1 ; return TRUE + +snp_exit: + pop ebx + pop edi + pop esi + mov esp, ebp + pop ebp + stdRET _HalStartNextProcessor + +stdENDP _HalStartNextProcessor + + +_TEXT ends ; end 32 bit code + + +_TEXT16 SEGMENT DWORD PUBLIC USE16 'CODE' ; start 16 bit code + + +;++ +; +; VOID +; StartPx_RMStub +; +; Routine Description: +; +; When a new processor is started, it starts in real-mode and is +; sent to a copy of this function which has been copied into low memory. +; (below 1MB and accessible from real-mode). +; +; Once CR0 has been set, this function jmp's to a StartPx_PMStub +; +; Arguments: +; none +; +; Return Value: +; does not return, jumps to StartPx_PMStub +; +;-- +cPublicProc StartPx_RMStub ,0 + cli + + db 066h ; load the GDT + lgdt fword ptr cs:[SPx_PB.PsSpecialRegisters.SrGdtr] + + db 066h ; load the IDT + lidt fword ptr cs:[SPx_PB.PsSpecialRegisters.SrIdtr] + + mov eax, cs:[SPx_TiledCR3] + mov cr3, eax + + mov ebp, dword ptr cs:[SPx_P0EBP] + mov ecx, dword ptr cs:[SPx_PB.PsContextFrame.CsSegDs] + mov ebx, dword ptr cs:[SPx_PB.PsSpecialRegisters.SrCr3] + mov eax, dword ptr cs:[SPx_PB.PsSpecialRegisters.SrCr0] + + mov cr0, eax ; into prot mode + + db 066h + db 0eah ; reload cs:eip +SPrxPMStub dd 0 +SPrxFlatCS dw 0 + +StartPx_RMStub_Len equ $ - StartPx_RMStub +stdENDP StartPx_RMStub + + +_TEXT16 ends ; End 16 bit code + +_TEXT SEGMENT ; Start 32 bit code + + +;++ +; +; VOID +; StartPx_PMStub +; +; Routine Description: +; +; This function completes the processor's state loading, and signals +; the requesting processor that the state has been loaded. +; +; Arguments: +; ebx - requested CR3 for this processors_state +; cx - requested ds for this processors_state +; ebp - EBP of P0 +; +; Return Value: +; does not return - completes the loading of the processors_state +; +;-- + align 16 ; to make sure we don't cross a page boundry + ; before reloading CR3 + +cPublicProc _StartPx_PMStub ,0 + + ; process is now in the load image copy of this function. + ; (ie, it's not the low memory copy) + + mov cr3, ebx ; get real CR3 + mov ds, cx ; set real ds + + lea esi, PxFrame.SPx_PB.PsSpecialRegisters + lldt word ptr ds:[esi].SrLdtr ; load ldtr + + ltr word ptr ds:[esi].SrTr ; load tss + lea edi, PxFrame.SPx_PB.PsContextFrame + + mov es, word ptr ds:[edi].CsSegEs ; Set other selectors + + mov fs, word ptr ds:[edi].CsSegFs + + mov gs, word ptr ds:[edi].CsSegGs + + mov ss, word ptr ds:[edi].CsSegSs + + add esi, SrKernelDr0 + .errnz (SrKernelDr1 - SrKernelDr0 - 1 * 4) + .errnz (SrKernelDr2 - SrKernelDr0 - 2 * 4) + .errnz (SrKernelDr3 - SrKernelDr0 - 3 * 4) + .errnz (SrKernelDr6 - SrKernelDr0 - 4 * 4) + .errnz (SrKernelDr7 - SrKernelDr0 - 5 * 4) + lodsd + mov dr0, eax ; load dr0-dr7 + lodsd + mov dr1, eax + lodsd + mov dr2, eax + lodsd + mov dr3, eax + lodsd + mov dr6, eax + lodsd + mov dr7, eax + + mov esp, dword ptr ds:[edi].CsEsp + mov esi, dword ptr ds:[edi].CsEsi + mov ecx, dword ptr ds:[edi].CsEcx + + push dword ptr ds:[edi].CsEflags + popfd ; load eflags + + push dword ptr ds:[edi].CsEip ; make a copy of remaining + push dword ptr ds:[edi].CsEax ; registers which need + push dword ptr ds:[edi].CsEbx ; loaded + push dword ptr ds:[edi].CsEdx + push dword ptr ds:[edi].CsEdi + push dword ptr ds:[edi].CsEbp + + inc [PxFrame.SPx_flag] ; Signal p0 that we are + ; done with it's data + ; Set remaining registers + pop ebp + pop edi + pop edx + pop ebx + pop eax + stdRET _StartPx_PMStub ; Set eip + +stdENDP _StartPx_PMStub + +_TEXT ends ; end 32 bit code + end diff --git a/private/ntos/nthals/halcbus/i386/cbushal.c b/private/ntos/nthals/halcbus/i386/cbushal.c new file mode 100644 index 000000000..c4e992ec5 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbushal.c @@ -0,0 +1,482 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + cbushal.c + +Abstract: + + + This module implements the initialization of the system dependent + functions that define the Hardware Architecture Layer (HAL) for an + x86 system. + +Author: + + David N. Cutler (davec) 25-Apr-1991 + +Environment: + + Kernel mode only. + +Revision History: + + Landy Wang (landy@corollary.com) 26-Mar-1992: + + - slight modifications for Corollary's symmetric interrupt enabling + +--*/ + +#include "halp.h" +#include "cbus_nt.h" // pick up APC_TASKPRI definition + +VOID +HalpInitializeCoreIntrs(VOID); + +PUCHAR +CbusFindString ( +IN PUCHAR Str, +IN PUCHAR StartAddr, +IN LONG Len +); + +BOOLEAN +HalInitSystem ( + IN ULONG Phase, + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ); + +BOOLEAN +CbusGetParameters ( + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, HalpInitializeCoreIntrs) +#pragma alloc_text(INIT, HalInitSystem) +#pragma alloc_text(INIT, CbusGetParameters) +#endif + +ULONG HalpBusType; + +extern ADDRESS_USAGE HalpDefaultPcIoSpace; +extern ADDRESS_USAGE HalpEisaIoSpace; + +ULONG +CbusStringLength ( +IN PUCHAR Str +); + +VOID +HalpInitBusHandlers (VOID); + +VOID +HalpInitOtherBuses (VOID); + +ULONG +HalpInitMP( + IN ULONG Phase, + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ); + +VOID +HalpDispatchInterrupt( VOID ); + +VOID +HalpApcInterrupt( VOID ); + +VOID +HalpIpiHandler( VOID ); + +VOID +HalpInitializeTimeIncrement( VOID ); + +VOID +HalpRegisterInternalBusHandlers( VOID ); + +extern ULONG CbusIpiVector; + +KSPIN_LOCK HalpSystemHardwareLock; + + +/*++ + +Routine Description: + + This function initializes the HAL-specific "software" (APC, DPC) + and hardware (IPI, spurious) interrupts for the Corollary architectures. + +Arguments: + + none. + +Return Value: + + VOID + +--*/ + +VOID +HalpInitializeCoreIntrs(VOID +) +{ + // + // Here we initialize all the core interrupts that need to + // work EARLY on in kernel startup. Device interrupts are + // not enabled until later (in HalInitSystem). + // + // Each processor needs to call KiSetHandlerAddressToIDT() + // and HalEnableSystemInterrupt() for himself. This is done + // as a by-product of the HAL IDT registration scheme. + // + // Even though race conditions can exist between processors as + // there is no interlocking when calling HalpRegisterVector() + // from HalpEnabledInterruptHandler(), this is not harmful in + // this particular case, as all processors will be writing the + // exact same values. + // + + HalpEnableInterruptHandler ( + DeviceUsage, // Mark as device vector + DPC_TASKPRI, // No real IRQ, so use this + DPC_TASKPRI, // System IDT + DISPATCH_LEVEL, // System Irql + HalpDispatchInterrupt, // ISR + Latched ); + + HalpEnableInterruptHandler ( + DeviceUsage, // Mark as device vector + APC_TASKPRI, // No real IRQ, so use this + APC_TASKPRI, // System IDT + APC_LEVEL, // System Irql + HalpApcInterrupt, // ISR + Latched ); + + HalpEnableInterruptHandler ( + DeviceUsage, // Mark as device vector + CbusIpiVector, // No real IRQ, so use this + CbusIpiVector, // System IDT + IPI_LEVEL, // System Irql + HalpIpiHandler, // ISR + Latched ); +} + + +BOOLEAN +CbusGetParameters ( + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ) +/*++ + +Routine Description: + + This gets any parameters from the boot.ini invocation line. + +Arguments: + + None. + +Return Value: + + TRUE if initial breakpoint was requested, FALSE otherwise + +--*/ +{ + PUCHAR Options; + ULONG OptionLength; + BOOLEAN InitialBreak = FALSE; + UCHAR IsBreak[] = "BREAK"; + + if (LoaderBlock != NULL && LoaderBlock->LoadOptions != NULL) { + Options = (PUCHAR)LoaderBlock->LoadOptions; + // + // Uppercase Only + // + + // + // The number of processors to boot can be specified dynamically + // with the /NUMPROC=n, and this will be parsed by the Executive. + // so we don't need to bother with it here. + // + + // + // Has the user asked for an initial BreakPoint (ie: /BREAK) ? + // + + OptionLength = CbusStringLength (Options); + if (CbusFindString(IsBreak, Options, OptionLength)) + InitialBreak = TRUE; + } + return InitialBreak; +} + +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; + extern VOID HalpAddMem(IN PLOADER_PARAMETER_BLOCK); + PKPRCB pPRCB; + ULONG BuildType; + + pPRCB = KeGetCurrentPrcb(); + + if (Phase == 0) { + + if (CbusGetParameters (LoaderBlock)) { + DbgBreakPoint(); + } + + HalpBusType = LoaderBlock->u.I386.MachineType & 0x00ff; + + // + // Verify Prcb version and build flags conform to + // this image + // + + BuildType = 0; +#if DBG + BuildType |= PRCB_BUILD_DEBUG; +#endif +#ifdef NT_UP + BuildType |= PRCB_BUILD_UNIPROCESSOR; +#endif + + if (pPRCB->MajorVersion != PRCB_MAJOR_VERSION) { + KeBugCheckEx (MISMATCHED_HAL, + 1, pPRCB->MajorVersion, PRCB_MAJOR_VERSION, 0); + } + + if (pPRCB->BuildType != BuildType) { + KeBugCheckEx (MISMATCHED_HAL, + 2, pPRCB->BuildType, BuildType, 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 + + // + // Most HALs initialize their PICs at this point - we set up + // the Cbus PICs in HalInitializeProcessor() long ago... + // Now that the PICs are initialized, we need to mask them to + // reflect the current Irql + // + + CurrentIrql = KeGetCurrentIrql(); + KfRaiseIrql(CurrentIrql); + + // + // Fill in handlers for APIs which this hal supports + // + + HalQuerySystemInformation = HaliQuerySystemInformation; + HalSetSystemInformation = HaliSetSystemInformation; + + // + // Initialize CMOS + // + + HalpInitializeCmos(); + + + // + // Register the PC-compatible base IO space used by hal + // + + HalpRegisterAddressUsage (&HalpDefaultPcIoSpace); + if (HalpBusType == MACHINE_TYPE_EISA) { + HalpRegisterAddressUsage (&HalpEisaIoSpace); + } + + // + // Cbus1: stall uses the APIC to figure it out (needed in phase0). + // the clock uses the APIC (needed in phase0) + // the perfcounter uses RTC irq8 (not needed till all cpus boot) + // + // Cbus2: stall uses the RTC irq8 to figure it out (needed in phase0). + // the clock uses the irq0 (needed in phase0) + // the perfcounter uses RTC irq8 (not needed till all cpus boot) + // + // + // set up the stall execution and enable clock interrupts now. + // APC, DPC and IPI are already enabled. + // + + (*CbusBackend->HalInitializeInterrupts)(0); + + HalStopProfileInterrupt(0); + + HalpInitializeDisplay(); + + // + // Initialize spinlock used by HalGetBusData hardware access routines + // + + KeInitializeSpinLock(&HalpSystemHardwareLock); + + // + // Any additional memory must be recovered BEFORE Phase0 ends + // + HalpAddMem(LoaderBlock); + + // + // 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->BasePage + Descriptor->PageCount > 0x1000) { + LessThan16Mb = FALSE; + } + + 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. + // + // This should probably create a memory descriptor which describes + // the DMA map buffers reserved by the HAL, and then add it back in + // to the LoaderBlock so the kernel can report the correct amount + // of memory in the machine. + // + + 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 - run by all processors eventually, + // however processor 0 runs here to completion _BEFORE_ any + // other processors have been brought out of reset. + // + PKPCR pPCR = KeGetPcr(); + + // + // each processor sets up his own global vectors. + // we do this here for hardware device interrupts, and + // enable IPI & SW interrupts from HalInitializeProcessor. + // + // Note that for Cbus1, InitializeClock MUST be called after + // HalpInitializeStallExecution, because HalpInitializeStallExecution + // reprograms the timer. + // + // The boot processor has already done all this as part of Phase 0, + // but each additional processor is responsible for setting up his + // own hardware, so the additional processors each do it here... + // + + if (pPCR->Prcb->Number == 0) { + + HalpRegisterInternalBusHandlers (); + + HalpInitOtherBuses (); + } + else { + + (*CbusBackend->HalInitializeInterrupts)(pPCR->Prcb->Number); + } + + // + // No need to enable irq13 for FP errors - all the Corollary + // architectures are 486 and above, so we will route FP errors + // through trap 0x10. + // + + } + + HalpInitMP (Phase, LoaderBlock); + + return TRUE; +} diff --git a/private/ntos/nthals/halcbus/i386/cbuslock.asm b/private/ntos/nthals/halcbus/i386/cbuslock.asm new file mode 100644 index 000000000..0e8e3c546 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbuslock.asm @@ -0,0 +1,512 @@ +if NT_INST + +else + TITLE "Spin Locks" +;++ +; +; Copyright (c) 1989 Microsoft Corporation +; +; Module Name: +; +; cbuslock.asm +; +; Abstract: +; +; This module implements x86 spinlock functions for the Corollary +; multiprocessor HAL. Including both a stripped-down RaiseIrql +; and LowerIrql inline for the AcquireSpinLock & ReleaseSpinLock +; routines for speed. +; +; Author: +; +; Bryan Willman (bryanwi) 13 Dec 89 +; +; Environment: +; +; Kernel mode only. +; +; Revision History: +; +; Ken Reneris (kenr) 22-Jan-1991 +; +; Landy Wang (landy@corollary.com) 07 Feb 93 +; - Now that these routines have been moved to the domain of the HAL, +; speed them up by coding raise/lower irql inline. +;-- + + PAGE + +.486p + + +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include hal386.inc +include mac386.inc +include i386\cbus.inc + + EXTRNP KfRaiseIrql,1,,FASTCALL + EXTRNP KfLowerIrql,1,,FASTCALL + EXTRNP _KeSetEventBoostPriority, 2, IMPORT + EXTRNP _KeWaitForSingleObject,5, IMPORT + +ifdef NT_UP + LOCK_ADD equ add + LOCK_DEC equ dec +else + LOCK_ADD equ lock add + LOCK_DEC equ lock dec +endif + +_TEXT SEGMENT PARA PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING + + PAGE + SUBTTL "Acquire Kernel Spin Lock" +;++ +; +; KIRQL +; FASTCALL +; KfAcquireSpinLock ( +; IN PKSPIN_LOCK SpinLock +; ) +; +; Routine Description: +; +; This function raises to DISPATCH_LEVEL and then acquires a the +; kernel spin lock. +; +; Arguments: +; +; (ecx) = SpinLock - Supplies a pointer to an kernel spin lock. +; +; Return Value: +; +; OldIrql - pointer to place old irql +; +; None. +; +;-- + +align 16 +cPublicFastCall KfAcquireSpinLock, 1 +cPublicFpo 0,0 + + ; + ; Raise to DISPATCH_LEVEL inline + ; + +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr + + mov edx, [eax] ; get old taskpri val + + mov dword ptr [eax], DPC_TASKPRI ; set new hardware taskpri +ifdef CBC_REV1 + popfd +endif + + mov eax, [_CbusVectorToIrql+4*edx] ; convert old taskpri to irql + + ; + ; Acquire the lock + ; + + align 4 +sl10: ACQUIRE_SPINLOCK ecx,<short sl20> + fstRET KfAcquireSpinLock + + ; + ; Lock is owned, spin till it looks free, then go get it again. + ; + + align 4 +sl20: SPIN_ON_SPINLOCK ecx,sl10 + +fstENDP KfAcquireSpinLock + + PAGE + SUBTTL "Acquire Synch Kernel Spin Lock" +;++ +; +; KIRQL +; FASTCALL +; 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) +; +; Arguments: +; +; (ecx) = SpinLock - Supplies a pointer to an kernel spin lock. +; +; Return Value: +; +; OldIrql - pointer to place old irql +; +;-- + +align 16 +cPublicFastCall KeAcquireSpinLockRaiseToSynch,1 +cPublicFpo 0,0 + +; +; Disable interrupts +; + +sls10: cli + +; +; Try to obtain spinlock. Use non-lock operation first +; + TEST_SPINLOCK ecx,<short sls20> + ACQUIRE_SPINLOCK ecx,<short sls20> + + +; +; Got the lock, raise to SYNCH_LEVEL +; + + ; this function should be optimized to perform the irql + ; operation inline. + + mov ecx, SYNCH_LEVEL + fstCall KfRaiseIrql ; (al) = OldIrql + +; +; Enable interrupts and return +; + + sti + fstRET KeAcquireSpinLockRaiseToSynch + + +; +; Lock is owned, spin till it looks free, then go get it again. +; + +sls20: sti + SPIN_ON_SPINLOCK ecx,sls10 + +fstENDP KeAcquireSpinLockRaiseToSynch + + + PAGE + SUBTTL "Release Kernel Spin Lock" + +;++ +; +; VOID +; FASTCALL +; KfReleaseSpinLock ( +; IN PKSPIN_LOCK SpinLock, +; IN KIRQL NewIrql +; ) +; +; Routine Description: +; +; This function releases a kernel spin lock and lowers to the new irql +; +; 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 + + movzx edx, dl + RELEASE_SPINLOCK ecx ; release it + mov ecx, [_CbusIrqlToVector+4*edx] ; convert irql to taskpri + + ; Inline version of KeLowerIrql + +ifdef CBC_REV1 + ; + ; we should be at DISPATCH_LEVEL on entry and therefore shouldn't + ; need scheduling protection, but add this anyway in case there + ; are places that call this routine that don't obey the rules... + ; + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get hardware taskpri addr + + mov [eax], ecx ; set new hardware taskpri + + ; + ; read back the new hardware taskpri to work around an APIC errata. + ; doing this read forces the write to the task priority above to get + ; flushed out of any write buffer it may be lying in. this is needed + ; to ensure that we get the interrupt immediately upon return, not + ; just at some point in the (distant) future. this is needed because + ; NT counts on this in various portions of the code, for example + ; in KeConnectInterrupt(), where the rescheduling DPC must arrive + ; within 12 assembly instructions after lowering IRQL. + ; + mov ecx, [eax] ; issue dummy read as per above +ifdef CBC_REV1 + popfd +endif + + fstRET KfReleaseSpinLock + +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,1 + + ; + ; code KfRaiseIrql/KfLowerIrql inline for speed + ; +ifdef CBC_REV1 + pushfd + cli +endif + mov edx, PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr + + mov eax, [edx] ; save old taskpri val + + mov [edx], APC_TASKPRI ; set new hardware taskpri + +ifdef CBC_REV1 + popfd +endif + mov eax, [_CbusVectorToIrql+4*eax] ; convert old taskpri to irql + + LOCK_DEC dword ptr [ecx].FmCount ; Get count + jz short afm_ret ; The owner? Yes, Done + + inc dword ptr [ecx].FmContention + +cPublicFpo 0,1 + push ecx ; save mutex address + push eax ; save entry irql + add ecx, FmEvent ; Wait on Event + stdCall _KeWaitForSingleObject,<ecx,WrExecutive,0,0,0> + pop eax ; restore entry irql + pop ecx ; restore mutex address + +cPublicFpo 0,0 +afm_ret: + mov byte ptr [ecx].FmOldIrql, al ; tell caller his entry irql + fstRet ExAcquireFastMutex + +fstENDP ExAcquireFastMutex + +;++ +; +; 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 + +; +; Try to acquire +; + cmp dword ptr [ecx].FmCount, 1 ; Busy? + jne short tam25 ; Yes, abort + +cPublicFpo 0,1 + + ; + ; code KfRaiseIrql/KfLowerIrql inline for speed + ; +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr + + mov edx, [eax] ; get old taskpri val + + mov [eax], APC_TASKPRI ; set new hardware taskpri + +ifdef CBC_REV1 + popfd +endif + + push edx ; Save old task priority + + mov eax, 1 ; Value to compare against + mov edx, 0 ; Value to set + + lock cmpxchg dword ptr [ecx].FmCount, edx ; Attempt to acquire + jnz short tam20 ; got it? + +cPublicFpo 0,0 + pop edx ; (edx) = old task priority + mov edx, [_CbusVectorToIrql+4*edx] ; convert old taskpri to irql + + mov eax, 1 ; return TRUE + mov byte ptr [ecx].FmOldIrql, dl ; Store OldIrql + fstRet ExTryToAcquireFastMutex + +tam20: + pop edx ; (edx) = old task priority + +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get hardware taskpri addr + + mov [eax], edx ; set new hardware taskpri + + ; + ; we must re-read the task priority register because this read + ; forces the write above to be flushed out of the write buffers. + ; otherwise the write above can get stuck and result in a pending + ; interrupt not being immediately delivered. in situations like + ; KeConnectInterrupt, the interrupt must be delivered in less than + ; 12 assembly instructions (the processor sends himself a rescheduling + ; DPC and has to execute it to switch to another CPU before continuing) + ; or corruption will result. because he thinks he has switched + ; processors and he really hasn't. and having the interrupt come in + ; after the 12 assembly instructions is _TOO LATE_!!! + ; + mov ecx, [eax] ; ensure it's lowered +ifdef CBC_REV1 + popfd +endif + +tam25: xor eax, eax ; return FALSE + fstRet ExTryToAcquireFastMutex ; all done + +fstENDP ExTryToAcquireFastMutex + + +;++ +; +; 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 + mov al, byte ptr [ecx].FmOldIrql ; (cl) = OldIrql + + LOCK_ADD dword ptr [ecx].FmCount, 1 ; Remove our count + xchg ecx, eax ; (cl) = OldIrql + js short rfm05 ; if < 0, set event + jnz short rfm06 ; if != 0, don't set event + +rfm05: add eax, FmEvent + push ecx + stdCall _KeSetEventBoostPriority, <eax, 0> + pop ecx +rfm06: + + ; + ; code KfLowerIrql inline for speed + ; + + movzx ecx, cl + mov ecx, [_CbusIrqlToVector+4*ecx] ; convert irql to taskpri + +ifdef CBC_REV1 + pushfd + cli +endif + mov eax, PCR[PcHal.PcrTaskpri] ; get hardware taskpri addr + + mov [eax], ecx ; set new hardware taskpri + + ; + ; we must re-read the task priority register because this read + ; forces the write above to be flushed out of the write buffers. + ; otherwise the write above can get stuck and result in a pending + ; interrupt not being immediately delivered. in situations like + ; KeConnectInterrupt, the interrupt must be delivered in less than + ; 12 assembly instructions (the processor sends himself a rescheduling + ; DPC and has to execute it to switch to another CPU before continuing) + ; or corruption will result. because he thinks he has switched + ; processors and he really hasn't. and having the interrupt come in + ; after the 12 assembly instructions is _TOO LATE_!!! + ; + mov ecx, [eax] ; ensure it's lowered +ifdef CBC_REV1 + popfd +endif + + fstRET ExReleaseFastMutex + +fstENDP ExReleaseFastMutex + +_TEXT ends +ENDIF ; NT_INST + + end diff --git a/private/ntos/nthals/halcbus/i386/cbusmem.c b/private/ntos/nthals/halcbus/i386/cbusmem.c new file mode 100644 index 000000000..f7ecf9266 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusmem.c @@ -0,0 +1,294 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbus_mem.c + +Abstract: + + This module implements the handling of additional memory + ranges to be freed to the MM subcomponent for the Corollary MP + architectures under Windows NT. + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + + +--*/ + +#include "halp.h" +#include "cbusrrd.h" + +VOID +CbusMemoryFree( +IN ULONG Address, +IN ULONG Size +); + +VOID +HalpAddMem ( + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, CbusMemoryFree) +#pragma alloc_text(INIT, HalpAddMem) +#endif + +#define MAX_MEMORY_RANGES 16 + +ULONG HalpMemoryIndex; + +MEMORY_ALLOCATION_DESCRIPTOR HalpMemory [MAX_MEMORY_RANGES]; + +extern EXT_CFG_OVERRIDE_T CbusGlobal; +extern ADDRESS_USAGE HalpCbusMemoryHole; +extern ULONG CbusMemoryHoleIndex; + +#define SIXTEEN_MB (BYTES_TO_PAGES(16 * 1024 * 1024)) + +VOID +CbusMemoryFree( +IN ULONG Address, +IN ULONG Size +) +/*++ + +Routine Description: + + Add the specified memory range to our list of memory ranges to + give to MM later. Called each time we find a memory card during startup, + also called on Cbus1 systems when we add a jumpered range (between 8 and + 16MB). ranges are jumpered via EISA config when the user has added a + dual-ported RAM card and wants to configure it into the middle of memory + (not including 640K-1MB, which is jumpered for free) somewhere. + +Arguments: + + Address - Supplies a start physical address in bytes of memory to free + + Size - Supplies a length in bytes spanned by this range + +Return Value: + + None. + +--*/ + +{ + PMEMORY_ALLOCATION_DESCRIPTOR Descriptor; + ULONG Page; + ULONG Index; + ULONG Index2; + + // + // add the card provided we have space. + // + if (HalpMemoryIndex >= MAX_MEMORY_RANGES) + return; + + Page = BYTES_TO_PAGES(Address); + for (Index = 0; Index < HalpMemoryIndex; Index++) { + if (Page < HalpMemory[Index].BasePage) { + for (Index2 = HalpMemoryIndex+1; Index2 > Index; Index2--) { + HalpMemory[Index2] = HalpMemory[Index2 - 1]; + } + break; + } + } + + Descriptor = &HalpMemory[Index]; + Descriptor->MemoryType = MemoryFree; + Descriptor->BasePage = Page; + Descriptor->PageCount = BYTES_TO_PAGES(Size); + HalpMemoryIndex++; +} + + +VOID +HalpAddMem ( + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ) + + +/*++ + +Routine Description: + + This function adds any general-purpose memory (not found by + ntldr) to the memory descriptor lists for usage by the MM subcomponent. + Kernel mode only. Called from HalInitSystem() at Phase0 since + this must all happen before MmInitSystem happens at Phase0. + + +Arguments: + + LoaderBlock data structure to add the memory to. Note that when + adding memory, no attempt is made to sort new entries numerically + into the list. + +Return Value: + + None. + +--*/ + +{ + PMEMORY_ALLOCATION_DESCRIPTOR Descriptor; + PLIST_ENTRY NewMd; + ULONG Index; + + // + // first, scan the existing memory list checking for an entries + // memory holes added to this table via the BIOS E820 function. + // using the E820 memory function was the only way to have the + // uniprocessor HAL not use the 3GB - 4GB range (CSR space), + // and other memory memory holes, for PCI devices. however, + // using the E820 memory function to pass reserved ranges has the + // side-effect of causing NT to allocate page tables and page + // file size calculations based on the inclusion of these reserved + // memory ranges. so, the E820 memory function will continue + // to set this range, but the C-bus HAL will look for these reserved + // memory ranges and delete it before the page tables are allocated. + // note that these reserved memory ranges have been recorded in + // HalpCbusMemoryHole and passed to the NT kernel as resources + // reserved by the C-bus HAL. + // + NewMd = LoaderBlock->MemoryDescriptorListHead.Flink; + + while (NewMd != &LoaderBlock->MemoryDescriptorListHead) { + Descriptor = CONTAINING_RECORD(NewMd, MEMORY_ALLOCATION_DESCRIPTOR, + ListEntry); + + for (Index = 0; Index < CbusMemoryHoleIndex; Index++) { + if (Descriptor->BasePage == BYTES_TO_PAGES(HalpCbusMemoryHole.Element[Index].Start)) { + NewMd = &Descriptor->ListEntry; + RemoveEntryList(NewMd); + break; + } + } + + NewMd = Descriptor->ListEntry.Flink; + } + + // + // next, scan the existing memory list for memory above 16MB. + // if we find any, then we are running on a new BIOS + // which has already told the NT loader about all the memory in + // the system, so don't free it again now... + // + NewMd = LoaderBlock->MemoryDescriptorListHead.Flink; + + while (NewMd != &LoaderBlock->MemoryDescriptorListHead) { + Descriptor = CONTAINING_RECORD(NewMd, MEMORY_ALLOCATION_DESCRIPTOR, + ListEntry); + + if (Descriptor->BasePage + Descriptor->PageCount > SIXTEEN_MB) { + // + // our BIOS gave NT loader the memory already, so + // we can just bail right now... + // + return; + } + + NewMd = Descriptor->ListEntry.Flink; + } + + Descriptor = HalpMemory; + + for (Index = 0; Index < HalpMemoryIndex; Index++, Descriptor++) { + + NewMd = &Descriptor->ListEntry; + + // + // any memory below 16MB has already been reported by the BIOS, + // so trim the request. this is because requests can arrive in + // the flavor of a memory board with 64MB (or more) on one board! + // + // note that Cbus1 "hole" memory is always reclaimed at the + // doubly-mapped address in high memory, never in low memory. + // + + if (Descriptor->BasePage + Descriptor->PageCount <= SIXTEEN_MB) { + continue; + } + + if (Descriptor->BasePage < SIXTEEN_MB) { + Descriptor->PageCount -= (SIXTEEN_MB-Descriptor->BasePage); + Descriptor->BasePage = SIXTEEN_MB; + } + + InsertHeadList(&LoaderBlock->MemoryDescriptorListHead, NewMd); + + } +} + +#if 0 + +VOID +CbusMemoryThread() +{ + // + // Loop looking for work to do + // + + do { + + // + // Wait until something is put in the queue. By specifying + // a wait mode of UserMode, the thread's kernel stack is + // swappable + // + + Entry = KeRemoveQueue(&ExWorkerQueue[QueueType], WaitMode, + NULL); + + WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List); + + // + // Execute the specified routine. + // + + } while (1); +} + +BOOLEAN +CbusCreateMemoryThread() +{ + HANDLE Thread; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + + // + // Create our memory scrubbing thread + // + + InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); + + Status = PsCreateSystemThread(&Thread, + THREAD_ALL_ACCESS, + &ObjectAttributes, + 0L, + NULL, + CbusMemoryThread, + (PVOID)CriticalWorkQueue); + + if (!NT_SUCCESS(Status)) { + return FALSE; + } + + ExCriticalWorkerThreads++; + ZwClose(Thread); + + return True; +} +#endif diff --git a/private/ntos/nthals/halcbus/i386/cbusmisc.asm b/private/ntos/nthals/halcbus/i386/cbusmisc.asm new file mode 100644 index 000000000..4223fcf3c --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusmisc.asm @@ -0,0 +1,406 @@ + + title "miscellaneous MP primitives for the Corollary MP machines" +;++ +; +;Copyright (c) 1992, 1993, 1994 Corollary Inc +; +;Module Name: +; +; cbusmisc.asm +; +;Abstract: +; +; This module contains miscellaneous MP primitives for the +; Corollary MP machines. +; +;Author: +; +; Landy Wang (landy@corollary.com) 26-Mar-1992 +; +;Revision History: +; +;-- + + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include cbus.inc + +; +; enable the Pentium internal cache to its full capability +; +CR0_INTERNAL_ON equ (not (CR0_NW or CR0_CD)) +CR0_INTERNAL_OFF equ (CR0_NW or CR0_CD) + + .list + +INIT SEGMENT DWORD PUBLIC 'CODE' ; Start 32 bit code + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + +;++ +; +; VOID +; i486cacheon +; +; Routine Description: +; +; This function enables the calling processor's internal cache. +; Executed by each processor (via HalInitializeProcessor) in the +; Corollary Cbus1 and Cbus2 architectures. +; +; Note: This must be run before HalpInitializeStallExecution(). +; +; Return Value: +; none. +; +;-- + +cPublicProc _i486cacheon ,0 + + pushfd + cli + + ; Enable the 486 processor internal cache if it wasn't already. + + mov eax, cr0 ; get real cr0 + test eax, CR0_INTERNAL_OFF ; see if CPU cache on already + jz short @f ; no op if it is + + ; hard code the WBINVD instruction to flush internal cache. + ; this would cause an opcode trap on 386, but we will ship + ; only 486 (Cbus1) and Pentium (Cbus2). + + db 00fh ; ESCAPE + db 009h ; write-back invalidate + + and eax, CR0_INTERNAL_ON ; enable CPU internal cache + jmp @f ; flush queues +@@: + mov cr0, eax ; put cr0 back + popfd + + stdRET _i486cacheon +stdENDP _i486cacheon + +;++ +; +; VOID +; i486cacheoff +; +; Routine Description: +; +; This function disables the calling processor's internal cache. +; Executed by each processor (via HalInitializeProcessor) in the +; Corollary Cbus1 and Cbus2 architectures. +; +; Note: This must be run before HalpInitializeStallExecution(). +; +; Return Value: +; none. +; +;-- + +cPublicProc _i486cacheoff ,0 + + pushfd + cli + + ; Disable the 486 processor internal cache if it wasn't already. + + mov eax, cr0 ; get real cr0 + test eax, CR0_INTERNAL_OFF ; see if CPU cache on already + jnz short @f ; no op if it is + + ; hard code the WBINVD instruction to flush internal cache. + ; this would cause an opcode trap on 386, but we will ship + ; only 486 (Cbus1) and Pentium (Cbus2). + + db 00fh ; ESCAPE + db 009h ; write-back invalidate + + or eax, CR0_INTERNAL_OFF ; disable CPU internal cache + jmp @f ; flush queues +@@: + mov cr0, eax ; put cr0 back + popfd + + stdRET _i486cacheoff +stdENDP _i486cacheoff + +;++ +; +; VOID +; CbusDefaultStall (VOID) +; +; Routine Description: +; +; This routine initializes the calling processor's stall execution to +; a reasonable value until we later give it a real value in HalInitSystem. +; +; Arguments: +; +; None. +; +; Return Value: +; +; None. +; +;-- + +cPublicProc _CbusDefaultStall ,0 + mov dword ptr PCR[PcStallScaleFactor], INITIAL_STALL_COUNT + stdRET _CbusDefaultStall +stdENDP _CbusDefaultStall + +INIT ends ; end 32 bit init code + +_TEXT SEGMENT PARA PUBLIC 'CODE' + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + page ,132 + subttl "KeQueryPerformanceCounter" +;++ +; +; 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. +; +;-- + + +cPublicProc _KeQueryPerformanceCounter ,1 + + jmp dword ptr [_CbusQueryPerformanceCounter] + +stdENDP _KeQueryPerformanceCounter + +;++ +; +; VOID +; HalCalibratePerformanceCounter ( +; IN volatile PLONG Number +; ) +; +; /*++ +; +; Routine Description: +; +; This routine calibrates the performance counter value for a +; multiprocessor system. The calibration can be done by zeroing +; the current performance counter, or by calculating a per-processor +; skewing between each processor's counter. +; +; Arguments: +; +; Number - Supplies a pointer to the count of the number of processors in +; the configuration. +; +; Return Value: +; +; None. +;-- +cPublicProc _HalCalibratePerformanceCounter,1 + + ; + ; Calibration is already handled by the Cbus HAL for both Cbus1 + ; and Cbus2. + ; + + stdRET _HalCalibratePerformanceCounter + +stdENDP _HalCalibratePerformanceCounter + + page ,132 + subttl "CbusRebootHandler" +;++ +; +; VOID +; CbusRebootHandler( +; VOID +; ); +; +; Routine Description: +; +; This routine is the interrupt handler for an IPI interrupt generated +; at a priority just below that of normal IPIs. Its function is to +; force all additional processors to flush their internal cache and halt. +; This puts the processors in a more conducive state for system reset. +; +; This routine is run only by the non-boot processors. +; +; Since this routine is entered directly via an interrupt gate, interrupt +; protection via cli is not necessary. +; +; Arguments: +; +; None +; +; Return Value: +; +; None. +; +;-- + + ENTER_DR_ASSIST hirs_a, hirs_t + +cPublicProc _CbusRebootHandler ,0 + + ; + ; Save machine state on trap frame + ; + + ENTER_INTERRUPT hirs_a, hirs_t + + ; keep it simple, just issue the EOI right now. + ; no changing of taskpri/irql is needed here. + ; Thus, the EOI serves as the HalEndSystemInterrupt. + + mov eax, _CbusRebootVector + CBUS_EOI eax, ecx ; destroy eax & ecx + + mov eax, dword ptr PCR[PcHal.PcrNumber] + + ; the boot processor will take care of the reboot from this point on. + ; however, ensure that our internal cache is flushed and halt. + + db 00fh ; ESCAPE + db 009h ; write-back invalidate + hlt + + ; + ; we should never reach this point, but if we do, just return our + ; processor number (loaded above). + ; + stdRET _CbusRebootHandler +stdENDP _CbusRebootHandler + + 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 + + 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 + + align 4 +@@: 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 + + align 4 +kese10: + stdRET _KeStallExecutionProcessor + +stdENDP _KeStallExecutionProcessor + +;++ +; +; ULONG +; HalSetTimeIncrement ( +; IN ULONG DesiredIncrement +; ) +; +; /*++ +; +; Routine Description: +; +; This routine initializes the system time clock to generate an +; interrupt at every DesiredIncrement interval. +; +; Arguments: +; +; DesiredIncrement - desired interval between every timer tick in +; 100ns units. +; +; Return Value: +; +; The *REAL* time increment set - this can be different from what he +; requested due to hardware limitations. +;-- + +cPublicProc _HalSetTimeIncrement,1 + + mov eax, _CbusBackend ; use hardware handler + jmp HalSetTimeIncrement[ eax ] ; JMP to set the interval + ; counter +stdENDP _HalSetTimeIncrement + +_TEXT ends ; end 32 bit code + + end diff --git a/private/ntos/nthals/halcbus/i386/cbusnls.h b/private/ntos/nthals/halcbus/i386/cbusnls.h new file mode 100644 index 000000000..2a23ee011 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusnls.h @@ -0,0 +1,41 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + cbusnls.h + +Abstract: + + Strings which are used in the HAL + + English + +--*/ + + +#define MSG_OBSOLETE "HAL: CBUS HAL.DLL cannot be run on an out-of-date Corollary machine.\n" +#define MSG_RRD_ERROR "HAL: RRD entry too short\n" +#define MSG_OBSOLETE_PIC "HAL: No advanced interrupt controller on boot CPU\nUse default Windows NT HAL instead\n" +#define MSG_OBSOLETE_PROC "WARNING: %d early C-bus I CPU board(s) disabled\n" + +#define MSG_ARBITRATE_ID_ERR "CbusIDtoCSR failure\n" +#define MSG_ARBITRATE_FAILED "Cbus1 CPU arbitration failed" + +#define MSG_NMI_ECC0 "NMI: Processor %d interrupt indication is 0\n" +#define MSG_NMI_ECC1 "NMI: Processor %d fault indication=%x\n" +#define MSG_NMI_ECC2 "NMI: Processor %d address=0x%x%x, quadword=%x, fault indication=%x\n" + +#define MSG_NMI_ECC3 "NMI: Memory board %d fault status is 0\n" +#define MSG_NMI_ECC4 "NMI: Memory board %d address=0x%x%x\n" + +#define MSG_NMI_ECC5 "NMI: I/O Bus bridge %d interrupt indication is 0\n" +#define MSG_NMI_ECC6 "NMI: I/O Bus bridge %d fault indication=%x\n" +#define MSG_NMI_ECC7 "NMI: I/O Bus bridge %d address=0x%x%x, quadword=%x, fault indication=%x\n" + +#define MSG_NMI_ECC8 "Fatal double-bit NMI" + +#define MSG_CBUS1NMI_ECC "NMI: double-bit ecc error address=0x%x%x\n" + +#define MSG_NEWLINE "\n" diff --git a/private/ntos/nthals/halcbus/i386/cbusnmi.c b/private/ntos/nthals/halcbus/i386/cbusnmi.c new file mode 100644 index 000000000..9ecbaaa7a --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusnmi.c @@ -0,0 +1,204 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + cbusnmi.c + +Abstract: + + Provides standard x86 NMI handler + +Author: + + kenr + +Revision History: + + Landy Wang (landy@corollary.com) 10-Oct-1992 + + - hook HalHandleNMI() so it can call the Corollary + backend platform-specific handlers. + +Revision History: + +--*/ +#ifndef _NTOS_ +#include "nthal.h" +#endif +#include "halp.h" +#include "cbus_nt.h" // C-bus NT-specific implementation stuff +#include "bugcodes.h" +#include "stdio.h" +#include "cbusnls.h" + +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: + + STATUS_SUCCESS if NMI was handled and system should continue, + BugCheck code if not. + +--*/ +{ + CbusBackend->ResolveNMI(NmiInfo); +} + + +#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 +DefaultHalHandleNMI( + 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: + + STATUS_SUCCESS if NMI was handled and system should continue, + BugCheck code if not. + +--*/ +{ + 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) { + c = (EisaPort > 9 ? 'A'-10 : '0') + EisaPort; + for (i=0; EisaNMIMsg[i]; i++) { + if (EisaNMIMsg[i] == '%') { + EisaNMIMsg[i] = c; + break; + } + } + HalDisplayString (EisaNMIMsg); + + } + } + } + } + + HalDisplayString (MSG_HALT); + KeEnterKernelDebugger(); +} + +VOID +CbusHardwareFailure( + IN PUCHAR HardwareMessage + ) +{ + HalDisplayString (MSG_HARDWARE_ERROR1); + HalDisplayString (MSG_HARDWARE_ERROR2); + HalDisplayString (HardwareMessage); + HalDisplayString (MSG_HALT); + KeEnterKernelDebugger(); +} diff --git a/private/ntos/nthals/halcbus/i386/cbusproc.c b/private/ntos/nthals/halcbus/i386/cbusproc.c new file mode 100644 index 000000000..199d2cc8f --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusproc.c @@ -0,0 +1,427 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbusproc.c + +Abstract: + + Corollary Start Next Processor C code. + + This module implements the initialization of the system dependent + functions that define the Hardware Architecture Layer (HAL) for the + Corollary MP architectures. + +Environment: + + Kernel mode only. + +Revision History: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + + - slight modifications for Corollary architecture in HalInitMP(). + + +--*/ + +#include "halp.h" +#include "cbus_nt.h" // C-bus NT-specific implementation definitions + +#ifndef MCA +UCHAR HalName[] = "Corollary C-bus Architecture MP HAL Version 3.4.0"; +#else +UCHAR HalName[] = "Corollary C-bus MicroChannel Architecture MP HAL Version 3.4.0"; +#endif + +VOID +HalpMapCR3 ( + IN ULONG VirtAddress, + IN PVOID PhysicalAddress, + IN ULONG Length + ); + +ULONG +HalpBuildTiledCR3 ( + IN PKPROCESSOR_STATE ProcessorState + ); + +VOID +HalpFreeTiledCR3 ( + VOID + ); + +VOID +HalpInitOtherBuses ( + VOID + ); + +BOOLEAN +HalpInitMP ( + IN ULONG Phase, + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ); + +VOID +HalpInitializePciBus ( + VOID + ); + +VOID +StartPx_PMStub( + VOID + ); + +VOID CbusCheckBusRanges( + VOID + ); + +VOID CbusInitializeOtherPciBus( + VOID + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,HalpInitMP) +#pragma alloc_text(INIT,HalAllProcessorsStarted) +#pragma alloc_text(INIT,HalReportResourceUsage) +#pragma alloc_text(INIT,HalReportResourceUsage) +#pragma alloc_text(INIT,HalpInitOtherBuses) +#pragma alloc_text(INIT,HalpFreeTiledCR3) +#pragma alloc_text(INIT,HalpMapCR3) +#pragma alloc_text(INIT,HalpBuildTiledCR3) +#endif + + +#define LOW_MEMORY 0x000100000 +#define MAX_PT 8 + +ULONG MpCount; // zero based. 0 = 1, 1 = 2, ... +PUCHAR MpLowStub; // pointer to low memory bootup stub +PVOID MpLowStubPhysicalAddress; // pointer to low memory bootup stub +PUCHAR MppIDT; // pointer to physical memory 0:0 +PVOID MpFreeCR3[MAX_PT]; // remember pool memory to free + + + +BOOLEAN +HalpInitMP ( + IN ULONG Phase, + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ) +/*++ + +Routine Description: + Allows MP initialization from HalInitSystem. + +Arguments: + Same as HalInitSystem + +Return Value: + none. + +--*/ +{ + extern ULONG CbusProcessors; + + if (Phase == 0) { + + MpCount = CbusProcessors-1; + + if (!MpCount) { + return TRUE; + } + + MppIDT = HalpMapPhysicalMemory (0, 1); + + // + // Allocate some low memory for processor bootup stub + // + + MpLowStubPhysicalAddress = (PVOID)HalpAllocPhysicalMemory (LoaderBlock, + LOW_MEMORY, 1, FALSE); + + if (!MpLowStubPhysicalAddress) + return TRUE; + + MpLowStub = (PCHAR) HalpMapPhysicalMemory (MpLowStubPhysicalAddress, 1); + + } + return TRUE; +} + +BOOLEAN +HalAllProcessorsStarted ( + VOID + ) +{ + return TRUE; +} + + +VOID +HalReportResourceUsage ( + VOID + ) +/*++ + +Routine Description: + The registry is now enabled - time to report resources which are + used by the HAL. + +Arguments: + +Return Value: + +--*/ +{ + 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); + + // + // Registry is now intialized, see if there are any PCI buses + // + + HalpInitializePciBus (); + + // + // Check for and initialize other PCI buses. + // + CbusInitializeOtherPciBus (); + + // + // Check all buses and determine SystemBase for all ranges + // within all buses. + // + CbusCheckBusRanges (); +} + +VOID +HalpInitOtherBuses (VOID) +{ + if (CbusBackend->InitOtherBuses) + (*CbusBackend->InitOtherBuses); +} + + + +ULONG +HalpBuildTiledCR3 ( + IN PKPROCESSOR_STATE ProcessorState + ) +/*++ + +Routine Description: + When the x86 processor is reset it starts in real-mode. In order to + move the processor from real-mode to protected mode with flat addressing + the segment which loads CR0 needs to have it's linear address mapped + to machine the phyiscal location of the segment for said instruction so + the processor can continue to execute the following instruction. + + This function is called to built such a tiled page directory. In + addition, other flat addresses are tiled to match the current running + flat address for the new state. Once the processor is in flat mode, + we move to a NT tiled page which can then load up the remaining processors + state. + +Arguments: + ProcessorState - The state the new processor should start in. + +Return Value: + Physical address of Tiled page directory + + +--*/ +{ +#define GetPdeAddress(va) ((PHARDWARE_PTE)((((((ULONG)(va)) >> 22) & 0x3ff) << 2) + (PUCHAR)MpFreeCR3[0])) +#define GetPteAddress(va) ((PHARDWARE_PTE)((((((ULONG)(va)) >> 12) & 0x3ff) << 2) + (PUCHAR)pPageTable)) + +// bugbug kenr 27mar92 - fix physical memory usage! + + MpFreeCR3[0] = ExAllocatePool (NonPagedPool, PAGE_SIZE); + RtlZeroMemory (MpFreeCR3[0], PAGE_SIZE); + + // + // Map page for real mode stub (one page) + // + HalpMapCR3 ((ULONG) MpLowStubPhysicalAddress, + MpLowStubPhysicalAddress, + PAGE_SIZE); + + // + // Map page for protect mode stub (one page) + // + HalpMapCR3 ((ULONG) &StartPx_PMStub, NULL, 0x1000); + + + // + // Map page(s) for processors GDT + // + HalpMapCR3 (ProcessorState->SpecialRegisters.Gdtr.Base, NULL, + ProcessorState->SpecialRegisters.Gdtr.Limit); + + + // + // Map page(s) for processors IDT + // + HalpMapCR3 (ProcessorState->SpecialRegisters.Idtr.Base, NULL, + ProcessorState->SpecialRegisters.Idtr.Limit); + + return MmGetPhysicalAddress (MpFreeCR3[0]).LowPart; +} + + +VOID +HalpMapCR3 ( + IN ULONG VirtAddress, + IN PVOID PhysicalAddress, + IN ULONG Length + ) +/*++ + +Routine Description: + Called to build a page table entry for the passed page directory. + Used to build a tiled page directory with real-mode & flat mode. + +Arguments: + VirtAddress - Current virtual address + PhysicalAddress - Optional. Physical address to be mapped to, if passed + as a NULL then the physical address of the passed + virtual address is assumed. + Length - number of bytes to map + +Return Value: + none. + +--*/ +{ + ULONG i; + PHARDWARE_PTE PTE; + PVOID pPageTable; + PHYSICAL_ADDRESS pPhysicalPage; + + + while (Length) { + PTE = GetPdeAddress (VirtAddress); + if (!PTE->PageFrameNumber) { + pPageTable = ExAllocatePool (NonPagedPool, PAGE_SIZE); + RtlZeroMemory (pPageTable, PAGE_SIZE); + + for (i=0; i<MAX_PT; i++) { + if (!MpFreeCR3[i]) { + MpFreeCR3[i] = pPageTable; + break; + } + } + ASSERT (i<MAX_PT); + + pPhysicalPage = MmGetPhysicalAddress (pPageTable); + PTE->PageFrameNumber = (pPhysicalPage.LowPart >> PAGE_SHIFT); + PTE->Valid = 1; + PTE->Write = 1; + } + + pPhysicalPage.LowPart = PTE->PageFrameNumber << PAGE_SHIFT; + pPhysicalPage.HighPart = 0; + pPageTable = MmMapIoSpace (pPhysicalPage, PAGE_SIZE, TRUE); + + PTE = GetPteAddress (VirtAddress); + + if (!PhysicalAddress) { + PhysicalAddress = (PVOID)MmGetPhysicalAddress ((PVOID)VirtAddress).LowPart; + } + + PTE->PageFrameNumber = ((ULONG) PhysicalAddress >> PAGE_SHIFT); + PTE->Valid = 1; + PTE->Write = 1; + + MmUnmapIoSpace (pPageTable, PAGE_SIZE); + + PhysicalAddress = 0; + VirtAddress += PAGE_SIZE; + if (Length > PAGE_SIZE) { + Length -= PAGE_SIZE; + } else { + Length = 0; + } + } +} + + + +VOID +HalpFreeTiledCR3 ( + VOID + ) +/*++ + +Routine Description: + Free's any memory allocated when the tiled page directory was built. + +Arguments: + none + +Return Value: + none +--*/ +{ + ULONG i; + + for (i=0; MpFreeCR3[i]; i++) { + ExFreePool (MpFreeCR3[i]); + MpFreeCR3[i] = 0; + } +} + + + + + +NTSTATUS +HalpGetMcaLog ( + OUT PMCA_EXCEPTION Exception, + OUT PULONG ReturnedLength + ) +{ + return STATUS_NOT_SUPPORTED; +} + +NTSTATUS +HalpMcaRegisterDriver( + IN PMCA_DRIVER_INFO DriverInfo + ) +{ + return STATUS_NOT_SUPPORTED; +} + + +ULONG +FASTCALL +HalSystemVectorDispatchEntry ( + IN ULONG Vector, + OUT PKINTERRUPT_ROUTINE **FlatDispatch, + OUT PKINTERRUPT_ROUTINE *NoConnection + ) +{ + return FALSE; +} diff --git a/private/ntos/nthals/halcbus/i386/cbusprof.asm b/private/ntos/nthals/halcbus/i386/cbusprof.asm new file mode 100644 index 000000000..4b445df95 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusprof.asm @@ -0,0 +1,193 @@ + title "profile interrupt handler for the Corollary MP machines" +;++ +; +;Copyright (c) 1992, 1993, 1994 Corollary Inc +; +;Module Name: +; +; cbusprof.asm +; +;Abstract: +; +; This module contains the profile interrupt handler for the +; non-boot processors in the Corollary MP machines. This is +; only needed because only one processor should ack the CMOS +; for each profile interrupt. +; +;Author: +; +; Landy Wang (landy@corollary.com) 26-Mar-1992 +; +;Revision History: +; +;-- + + + +.386p + .xlist +include hal386.inc +include callconv.inc ; calling convention macros +include i386\kimacro.inc +include cbus.inc + + EXTRNP _HalBeginSystemInterrupt,3 + EXTRNP _HalEndSystemInterrupt,2 +ifdef CBC_REV1 + EXTRNP _Cbus2RequestSoftwareInterrupt,1 +endif + + .list + + +_TEXT SEGMENT DWORD PUBLIC 'CODE' ; Start 32 bit code + ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING + + + page ,132 + subttl "System Profile Interrupt for Additional Processors" +;++ +; +; Routine Description: +; +; This routine is entered as the result of a profile interrupt. +; This routine is only entered by the non-boot processors. +; +; The CMOS will be acked by the boot processor (only one CPU can +; do this - which one is arbitrary, but it must only be ONE!). +; +; This function thus, just raises system Irql to PROFILE_LEVEL +; and transfers 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 +; +;-- + ENTER_DR_ASSIST Hpx_a, Hpx_t + +cPublicProc _HalpProfileInterruptPx ,0 + + ; + ; Save machine state in trap frame + ; + + ENTER_INTERRUPT Hpx_a, Hpx_t + + ; + ; (esp) - base of trap frame + ; + + push _ProfileVector + sub esp, 4 ; allocate space to save OldIrql + stdCall _HalBeginSystemInterrupt, <PROFILE_LEVEL,_ProfileVector,esp> + + ; + ; if this is C-bus II, modify the vector so the interrupt + ; will not be EOI'd. + ; + cmp _Cbus2BridgesFound, 0 + je short cbus1 + mov eax, [_CbusClockVector] + mov [esp+4], eax +cbus1: + + ; + ; The boot processor has already cleared (or is about to clear) + ; the interrupt flag on the RTC, so we don't do that here. + ; + + jmp _HalpProfileInterrupt2ndEntry@0 + +stdENDP _HalpProfileInterruptPx + + + page ,132 + subttl "System Clock Interrupt for Additional Processors" +;++ +; +; Routine Description: +; +; This routine is entered as the result of an interrupt generated by CLOCK. +; This routine is entered only by additional (non-boot) processors, so it +; must NOT update the performance counter or update the system time. +; +; instead, it just dismisses the interrupt, raises system Irql to +; CLOCK2_LEVEL and transfers control to the standard system routine +; to update the execution time of the current thread and process. +; +; Arguments: +; +; None +; Interrupt is disabled +; +; Return Value: +; +; Does not return, jumps directly to KeUpdateRunTime, which returns +; +; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt +; +;-- + ENTER_DR_ASSIST Hcix_a, Hcix_t + +cPublicProc _CbusClockInterruptPx ,0 + + ; + ; Save machine state in trap frame + ; (esp) - base of trap frame + ; + + ENTER_INTERRUPT Hcix_a, Hcix_t + +ifdef CBC_REV1 + ; + ; because we can miss an interrupt due to a hardware bug in the + ; CBC rev 1 silicon, send ourselves an IPI on every clock. + ; since we don't know when we've missed one, this will ensure + ; we don't cause lock timeouts if nothing else! + ; + + stdCall _Cbus2RequestSoftwareInterrupt, <IPI_LEVEL> +endif + + ; + ; Dismiss interrupt and raise irq level to clock2 level + ; + + push _CbusClockVector + sub esp, 4 ; allocate space to save OldIrql + stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL, _CbusClockVector, esp> + + ; Spurious interrupts on Corollary hardware are + ; directed to a different interrupt gate, so no need + ; to check return value above. + + POKE_LEDS eax, edx + + mov eax, dword ptr [_CbusTimeStamp] + mov dword ptr [eax], 0 + + ; + ; (esp) = OldIrql + ; (esp+4) = Vector + ; (esp+8) = base of trap frame + ; + ; (ebp) = base of trap frame for KeUpdateRunTime, this was set + ; up by the ENTER_INTERRUPT macro above + + stdCall _KeUpdateRunTime,<dword ptr [esp]> + + INTERRUPT_EXIT + +stdENDP _CbusClockInterruptPx + +_TEXT ends ; end 32 bit code + end diff --git a/private/ntos/nthals/halcbus/i386/cbusrrd.h b/private/ntos/nthals/halcbus/i386/cbusrrd.h new file mode 100644 index 000000000..e29a89cfd --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/cbusrrd.h @@ -0,0 +1,299 @@ +/*++ + +Copyright (c) 1992, 1993, 1994 Corollary Inc. + +Module Name: + + cbusrrd.h + +Abstract: + + Definitions for the Corollary C-bus II MP hardware architecture + interface with the Rom Resident Diagnostic + +Author: + + Landy Wang (landy@corollary.com) 26-Mar-1992 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#ifndef _CBUS_RRD_H +#define _CBUS_RRD_H + +// +// Processor Types - counting number +/// +#define PT_NO_PROCESSOR 0x0 +#define PT_386 0x1 +#define PT_486 0x2 +#define PT_PENTIUM 0x3 + +// +// Processor Attributes - counting number +/// +#define PA_CACHE_OFF 0x0 +#define PA_CACHE_ON 0x1 + +// +// I/O Function - counting number +// +#define IOF_NO_IO 0x0 +#define IOF_CBUS1_SIO 0x1 +#define IOF_CBUS1_SCSI 0x2 +#define IOF_REACH_IO 0x3 +#define IOF_ISA_BRIDGE 0x4 +#define IOF_EISA_BRIDGE 0x5 +#define IOF_HODGE 0x6 +#define IOF_MEDIDATA 0x7 +#define IOF_INVALID_ENTRY 0x8 // use to denote whole entry is invalid, + // note that pm must equal zero as well. +#define IOF_MEMORY 0x9 + +// +// Bit fields of pel_features, independent of whether pm indicates it +// has an attached processor or not. +// +#define ELEMENT_SIO 0x00001 // SIO present +#define ELEMENT_SCSI 0x00002 // SCSI present +#define ELEMENT_IOBUS 0x00004 // IO bus is accessible +#define ELEMENT_BRIDGE 0x00008 // IO bus Bridge +#define ELEMENT_HAS_8259 0x00010 // local 8259s present +#define ELEMENT_HAS_CBC 0x00020 // local Corollary CBC +#define ELEMENT_HAS_APIC 0x00040 // local Intel APIC +#define ELEMENT_WITH_IO 0x00080 // some extra I/O device here + // this could be SCSI, SIO, etc +#define ELEMENT_RRD_RESERVED 0x20000 // Old RRDs used this + +// Due to backwards compatibility, the check for an I/O +// device is somewhat awkward. + +#define ELEMENT_HAS_IO (ELEMENT_SIO | ELEMENT_SCSI | ELEMENT_WITH_IO) + +// +// Bit fields of machine types +// +#define MACHINE_CBUS1 0x1 // Original C-bus 1 +#define MACHINE_CBUS1_XM 0x2 // XM C-bus 1 +#define MACHINE_CBUS2 0x4 // C-bus 2 + +// +// Bit fields of supported environment types - each bit signifies that +// the specified operating system release is supported in full multiprocessor +// mode. Note that since the Cbus2 hardware changed, and initial hardware +// wasn't available until Q2 1994, Cbus2 RRDs will _NEVER_ set the +// 0x4 bit, and will instead set the 0x10 bit. This will have the effect +// of Cbus2 being supported in MP mode by NT release 3.5 and up. Cbus1 +// will be supported in MP mode by all NT releases (3.1 and up). +// +#define SCO_UNIX 0x01 +#define USL_UNIX 0x02 +#define WINDOWS_NT 0x04 // release 3.1 and up (July 1993) +#define NOVELL 0x08 +#define OS2 0x10 +#define WINDOWS_NT_R2 0x20 // release 3.5 and up (June 1994) + +// +// address of configuration passed +// +#define RRD_RAM 0xE0000 + +// +// extended structures passed by RRD ROMs to various kernels +// for the Corollary smp architectures +// +// layout of information passed to the kernels: +// The exact format of the configuration structures is hard +// coded in info.s (rrd). The layout is designed such that +// the ROM version need not be in sync with the kernel version. +// +// checkword: ULONG +// - extended configuration list must be terminated +// with EXT_CFG_END (0) +// length: ULONG +// - length is for structure body only; does not include +// either the checkword or length word +// +// structure body: format determined by checkword +// +// + +typedef struct _ext_cfg_header { + + ULONG ext_cfg_checkword; + ULONG ext_cfg_length; + +} EXT_CFG_HEADER_T, *PEXT_CFG_HEADER; + +// +// slot parameter structure (overrides any matching previous entry, +// but is usually used in conjunction with the ext_cfg_override) +// in processor_configuration or ext_memory_board. +// +// checkword is EXT_ID_INFO +// +// each structure is 16 bytes wide, and any number +// of these structures can be presented by the ROM. +// the kernel will keep reading them until either: +// +// a) an entry with id == 0x7f (this is treated as the list delimiter) +// OR +// b) the kernel's internal tables fill up. at which point, only +// the entries read thus far will be used and the rest ignored. +// +#define EXT_ID_INFO 0x01badcab +typedef struct _ext_id_info { + + ULONG id:7; + + // + // pm == 1 indicates CPU, pm == 0 indicates non-CPU (ie: memory or I/O) + // + ULONG pm:1; + + ULONG proc_type:4; + ULONG proc_attr:4; + + // + // io_function != 0 indicates I/O, + // io_function == 0 or 9 indicates memory + // + ULONG io_function:8; + + // + // io_attr can pertain to an I/O card or memory card + // + ULONG io_attr:8; + + // + // pel_start & pel_size can pertain to a CPU card, + // I/O card or memory card + // + ULONG pel_start; + ULONG pel_size; + + ULONG pel_features; + + // + // below two fields can pertain to an I/O card or memory card + // + ULONG io_start; + ULONG io_size; + +} EXT_ID_INFO_T, *PEXT_ID_INFO; + +#define LAST_EXT_ID 0x7f // delimit the extended ID list + +extern ULONG cbus_valid_ids; +extern EXT_ID_INFO_T cbusext_id_info[]; + +// +// configuration parameter override structure +// +// checkword is EXT_CFG_OVERRIDE. +// can be any length up to the kernel limit. this +// is a SYSTEMWIDE configuration override structure. +// +#define EXT_CFG_OVERRIDE 0xdeedcafe + +typedef struct _ext_cfg_override { + ULONG baseram; + ULONG memory_ceiling; + ULONG resetvec; + ULONG cbusio; // base of global Cbus I/O space + + UCHAR bootid; + UCHAR useholes; + UCHAR rrdarb; + UCHAR nonstdecc; + ULONG smp_creset; + ULONG smp_creset_val; + ULONG smp_sreset; + + ULONG smp_sreset_val; + + // + // piggyback various fields which have meaning only in Cbus2 with + // fields which only have meaning in Cbus1. these should really be + // unions... + // + + ULONG smp_contend; +#define InterruptControlMask smp_contend + + ULONG smp_contend_val; +#define FaultControlMask smp_contend_val + +#define CBUS_ENABLED_PW 0x01 +#define CBUS_ENABLED_LIG 0x02 +#define CBUS_DISABLE_LEVEL_TRIGGERED_INT_FIX 0x04 +#define CBUS_DISABLE_SPURIOUS_CLOCK_CHECK 0x08 +#define CBUS_ENABLE_BROADCAST 0x10 + + // + // if rrdarb is set (will only happen on Cbus1 machines), + // then use setida as the address to set CPU IDs. + // if rrdarb is not set _AND_ it's a Cbus2 machine, + // then if setida has bit 0 set, then enable + // posted-writes for EISA I/O cycles. + // + ULONG smp_setida; +#define Cbus2Features smp_setida + + ULONG smp_setida_val; +#define Control8259Mode smp_setida_val + + ULONG smp_cswi; +#define Control8259ModeValue smp_cswi + + ULONG smp_cswi_val; + ULONG smp_sswi; + + ULONG smp_sswi_val; + ULONG smp_cnmi; + ULONG smp_cnmi_val; + ULONG smp_snmi; + + ULONG smp_snmi_val; + ULONG smp_sled; + ULONG smp_sled_val; + ULONG smp_cled; + + ULONG smp_cled_val; + ULONG machine_type; + ULONG supported_environments; + ULONG broadcast_id; + +} EXT_CFG_OVERRIDE_T, *PEXT_CFG_OVERRIDE; + +extern EXT_CFG_OVERRIDE_T CbusGlobal; + +#define EXT_CFG_END 0 + +// +// this is the original structure passed from RRD to UNIX for the +// Corollary multiprocessor architecture. The only fields we are +// still interested in is the jumper settings - all other fields +// are now obtained from the extended configuration tables. hence +// the structure below contains only a subset of the original structure. +// + +#define ATMB 16 +#define MB(x) ((x) * 1024 * 1024) + +typedef struct _rrd_configuration { + + ULONG checkword; // must be 0xdeadbeef + UCHAR mem[64]; // each 1 signifies a real MB + UCHAR jmp[ATMB]; // each 1 signifies jumpered MB + +} RRD_CONFIGURATION_T, *PRRD_CONFIGURATION; + +#define JUMPER_SIZE (sizeof (RRD_CONFIGURATION_T)) + +#endif // _CBUS_RRD_H diff --git a/private/ntos/nthals/halcbus/i386/halp.h b/private/ntos/nthals/halcbus/i386/halp.h new file mode 100644 index 000000000..a9dbf1e13 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/halp.h @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "..\halx86\i386\halp.h" diff --git a/private/ntos/nthals/halcbus/i386/ix8259.inc b/private/ntos/nthals/halcbus/i386/ix8259.inc new file mode 100644 index 000000000..1546408d1 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ix8259.inc @@ -0,0 +1,5 @@ +; +; Include code from halx86 +; This is a cpp style symbolic link + +include halx86\i386\ix8259.inc diff --git a/private/ntos/nthals/halcbus/i386/ixbeep.asm b/private/ntos/nthals/halcbus/i386/ixbeep.asm new file mode 100644 index 000000000..eb2723e1c --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixbeep.asm @@ -0,0 +1,5 @@ +; +; Include code from halx86 +; This is a cpp style symbolic link + +include halx86\i386\ixbeep.asm diff --git a/private/ntos/nthals/halcbus/i386/ixbusdat.c b/private/ntos/nthals/halcbus/i386/ixbusdat.c new file mode 100644 index 000000000..38ff4f515 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixbusdat.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixbusdat.c" diff --git a/private/ntos/nthals/halcbus/i386/ixcmos.asm b/private/ntos/nthals/halcbus/i386/ixcmos.asm new file mode 100644 index 000000000..7c472d779 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixcmos.asm @@ -0,0 +1,5 @@ +; +; Include code from halx86 +; This is a cpp style symbolic link + +include halx86\i386\ixcmos.asm diff --git a/private/ntos/nthals/halcbus/i386/ixcmos.inc b/private/ntos/nthals/halcbus/i386/ixcmos.inc new file mode 100644 index 000000000..14a90be8f --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixcmos.inc @@ -0,0 +1,5 @@ +; +; Include code from halx86 +; This is a cpp style symbolic link + +include halx86\i386\ixcmos.inc diff --git a/private/ntos/nthals/halcbus/i386/ixdat.c b/private/ntos/nthals/halcbus/i386/ixdat.c new file mode 100644 index 000000000..b35be0b4b --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixdat.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixdat.c" diff --git a/private/ntos/nthals/halcbus/i386/ixenvirv.c b/private/ntos/nthals/halcbus/i386/ixenvirv.c new file mode 100644 index 000000000..998131ad7 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixenvirv.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixenvirv.c" diff --git a/private/ntos/nthals/halcbus/i386/ixfirm.c b/private/ntos/nthals/halcbus/i386/ixfirm.c new file mode 100644 index 000000000..f4a62c8e6 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixfirm.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixfirm.c" diff --git a/private/ntos/nthals/halcbus/i386/ixhwsup.c b/private/ntos/nthals/halcbus/i386/ixhwsup.c new file mode 100644 index 000000000..a38cb695a --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixhwsup.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixhwsup.c" diff --git a/private/ntos/nthals/halcbus/i386/ixidle.asm b/private/ntos/nthals/halcbus/i386/ixidle.asm new file mode 100644 index 000000000..df0471dbc --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixidle.asm @@ -0,0 +1,5 @@ +; +; Include code from halx86 +; This is a cpp style symbolic link + +include halx86\i386\ixidle.asm diff --git a/private/ntos/nthals/halcbus/i386/ixinfo.c b/private/ntos/nthals/halcbus/i386/ixinfo.c new file mode 100644 index 000000000..4b2ca3f27 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixinfo.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixinfo.c" diff --git a/private/ntos/nthals/halcbus/i386/ixisa.h b/private/ntos/nthals/halcbus/i386/ixisa.h new file mode 100644 index 000000000..851f7455d --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixisa.h @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixisa.h" diff --git a/private/ntos/nthals/halcbus/i386/ixisabus.c b/private/ntos/nthals/halcbus/i386/ixisabus.c new file mode 100644 index 000000000..69acc0d9b --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixisabus.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixisabus.c" diff --git a/private/ntos/nthals/halcbus/i386/ixisasup.c b/private/ntos/nthals/halcbus/i386/ixisasup.c new file mode 100644 index 000000000..441740a6f --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixisasup.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixisasup.c" diff --git a/private/ntos/nthals/halcbus/i386/ixkdcom.c b/private/ntos/nthals/halcbus/i386/ixkdcom.c new file mode 100644 index 000000000..a345dacea --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixkdcom.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixkdcom.c" diff --git a/private/ntos/nthals/halcbus/i386/ixkdcom.h b/private/ntos/nthals/halcbus/i386/ixkdcom.h new file mode 100644 index 000000000..561f64cc0 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixkdcom.h @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixkdcom.h" diff --git a/private/ntos/nthals/halcbus/i386/ixmca.h b/private/ntos/nthals/halcbus/i386/ixmca.h new file mode 100644 index 000000000..ee6b0709d --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixmca.h @@ -0,0 +1,5 @@ +// +// Include code from halmca +// This is a cpp style symbolic link + +#include "halmca\i386\ixmca.h" diff --git a/private/ntos/nthals/halcbus/i386/ixmcabus.c b/private/ntos/nthals/halcbus/i386/ixmcabus.c new file mode 100644 index 000000000..a8d8f55e0 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixmcabus.c @@ -0,0 +1,5 @@ +// +// Include code from halmca +// This is a cpp style symbolic link + +#include "halmca\i386\ixmcabus.c" diff --git a/private/ntos/nthals/halcbus/i386/ixmcasup.c b/private/ntos/nthals/halcbus/i386/ixmcasup.c new file mode 100644 index 000000000..f3e648e63 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixmcasup.c @@ -0,0 +1,5 @@ +// +// Include code from halmca +// This is a cpp style symbolic link + +#include "halmca\i386\ixmcasup.c" diff --git a/private/ntos/nthals/halcbus/i386/ixpcibrd.c b/private/ntos/nthals/halcbus/i386/ixpcibrd.c new file mode 100644 index 000000000..158e19341 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixpcibrd.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixpcibrd.c" diff --git a/private/ntos/nthals/halcbus/i386/ixpcibus.c b/private/ntos/nthals/halcbus/i386/ixpcibus.c new file mode 100644 index 000000000..9c799947b --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixpcibus.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixpcibus.c" diff --git a/private/ntos/nthals/halcbus/i386/ixpciint.c b/private/ntos/nthals/halcbus/i386/ixpciint.c new file mode 100644 index 000000000..35160bc76 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixpciint.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixpciint.c" diff --git a/private/ntos/nthals/halcbus/i386/ixphwsup.c b/private/ntos/nthals/halcbus/i386/ixphwsup.c new file mode 100644 index 000000000..90ef815af --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixphwsup.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixphwsup.c" diff --git a/private/ntos/nthals/halcbus/i386/ixprofil.asm b/private/ntos/nthals/halcbus/i386/ixprofil.asm new file mode 100644 index 000000000..07ad0d885 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixprofil.asm @@ -0,0 +1,5 @@ +; +; Include code from halx86 +; This is a cpp style symbolic link + +include halx86\i386\ixprofil.asm diff --git a/private/ntos/nthals/halcbus/i386/ixreboot.c b/private/ntos/nthals/halcbus/i386/ixreboot.c new file mode 100644 index 000000000..a246dd95d --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixreboot.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixreboot.c" diff --git a/private/ntos/nthals/halcbus/i386/ixthunk.c b/private/ntos/nthals/halcbus/i386/ixthunk.c new file mode 100644 index 000000000..01fe25363 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixthunk.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixthunk.c" diff --git a/private/ntos/nthals/halcbus/i386/ixusage.c b/private/ntos/nthals/halcbus/i386/ixusage.c new file mode 100644 index 000000000..524705910 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/ixusage.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\ixusage.c" diff --git a/private/ntos/nthals/halcbus/i386/pcip.h b/private/ntos/nthals/halcbus/i386/pcip.h new file mode 100644 index 000000000..476bab1e4 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/pcip.h @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "..\halx86\i386\pcip.h" diff --git a/private/ntos/nthals/halcbus/i386/xxbiosa.asm b/private/ntos/nthals/halcbus/i386/xxbiosa.asm new file mode 100644 index 000000000..2da2d0fd7 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/xxbiosa.asm @@ -0,0 +1,5 @@ +; +; Include code from halx86 +; This is a cpp style symbolic link + +include halx86\i386\xxbiosa.asm diff --git a/private/ntos/nthals/halcbus/i386/xxbiosc.c b/private/ntos/nthals/halcbus/i386/xxbiosc.c new file mode 100644 index 000000000..aa00832e9 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/xxbiosc.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\xxbiosc.c" diff --git a/private/ntos/nthals/halcbus/i386/xxdisp.c b/private/ntos/nthals/halcbus/i386/xxdisp.c new file mode 100644 index 000000000..451ee18e6 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/xxdisp.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\xxdisp.c" diff --git a/private/ntos/nthals/halcbus/i386/xxflshbf.c b/private/ntos/nthals/halcbus/i386/xxflshbf.c new file mode 100644 index 000000000..8a8646f63 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/xxflshbf.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\xxflshbf.c" diff --git a/private/ntos/nthals/halcbus/i386/xxkdsup.c b/private/ntos/nthals/halcbus/i386/xxkdsup.c new file mode 100644 index 000000000..2cad9cf92 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/xxkdsup.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\xxkdsup.c" diff --git a/private/ntos/nthals/halcbus/i386/xxmemory.c b/private/ntos/nthals/halcbus/i386/xxmemory.c new file mode 100644 index 000000000..a8c6f6f68 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/xxmemory.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\xxmemory.c" diff --git a/private/ntos/nthals/halcbus/i386/xxstubs.c b/private/ntos/nthals/halcbus/i386/xxstubs.c new file mode 100644 index 000000000..d0b4e936c --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/xxstubs.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\xxstubs.c" diff --git a/private/ntos/nthals/halcbus/i386/xxtime.c b/private/ntos/nthals/halcbus/i386/xxtime.c new file mode 100644 index 000000000..0767298a6 --- /dev/null +++ b/private/ntos/nthals/halcbus/i386/xxtime.c @@ -0,0 +1,5 @@ +// +// Include code from halx86 +// This is a cpp style symbolic link + +#include "halx86\i386\xxtime.c" diff --git a/private/ntos/nthals/halcbus/mca/makefile b/private/ntos/nthals/halcbus/mca/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/nthals/halcbus/mca/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/nthals/halcbus/mca/makefile.inc b/private/ntos/nthals/halcbus/mca/makefile.inc new file mode 100644 index 000000000..e2bb55a56 --- /dev/null +++ b/private/ntos/nthals/halcbus/mca/makefile.inc @@ -0,0 +1,2 @@ +obj\i386\hal.def: ..\..\hal.src + $(TARGET_CPP) /EP -Di386 $(C_DEFINES) ..\..\hal.src > obj\i386\hal.def diff --git a/private/ntos/nthals/halcbus/mca/sources b/private/ntos/nthals/halcbus/mca/sources new file mode 100644 index 000000000..f160571e9 --- /dev/null +++ b/private/ntos/nthals/halcbus/mca/sources @@ -0,0 +1,116 @@ + +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=hal + +TARGETNAME=halcbusm +TARGETPATH=$(BASEDIR)\public\sdk\lib + +!IF $(386) + +TARGETTYPE=HAL +NT_UP=0 + +!ELSE + +TARGETTYPE=DRIVER + +!ENDIF + +C_DEFINES=$(C_DEFINES) -DMCA +ASM_DEFINES=$(ASM_DEFINES) -DMCA + +INCLUDES=..\..\..\inc;..\..\..\ke;..\..\;..;..\..\halmca\i386 + +SOURCES= + +i386_SOURCES=hal.rc \ + drivesup.c \ + bushnd.c \ + rangesup.c \ + ..\i386\ixbeep.asm \ + ..\i386\ixbusdat.c \ + ..\i386\ixdat.c \ + ..\i386\ixinfo.c \ + ..\i386\ixisabus.c \ + ..\i386\ixpcibus.c \ + ..\i386\ixpciint.c \ + ..\i386\ixpcibrd.c \ + ..\i386\ixcmos.asm \ + ..\i386\ixenvirv.c \ + ..\i386\ixfirm.c \ + ..\i386\ixhwsup.c \ + ..\i386\ixidle.asm \ + ..\i386\ixmcabus.c \ + ..\i386\ixmcasup.c \ + ..\i386\ixkdcom.c \ + ..\i386\ixphwsup.c \ + ..\i386\ixprofil.asm \ + ..\i386\ixreboot.c \ + ..\i386\ixthunk.c \ + ..\i386\ixusage.c \ + ..\i386\xxbiosa.asm \ + ..\i386\xxbiosc.c \ + ..\i386\xxdisp.c \ + ..\i386\xxkdsup.c \ + ..\i386\xxmemory.c \ + ..\i386\xxstubs.c \ + ..\i386\xxtime.c \ + ..\i386\cbapic.c \ + ..\i386\cbioacc.asm \ + ..\i386\cb1stall.asm \ + ..\i386\cb2stall.asm \ + ..\i386\cbswint.asm \ + ..\i386\cbsysint.asm \ + ..\i386\cbmapint.c \ + ..\i386\cbdriver.c \ + ..\i386\cbusnmi.c \ + ..\i386\cbusboot.asm \ + ..\i386\cbuslock.asm \ + ..\i386\cbusprof.asm \ + ..\i386\cbusmisc.asm \ + ..\i386\cbus2cbc.asm \ + ..\i386\cbusapic.asm \ + ..\i386\cbus2.c \ + ..\i386\cbus1.c \ + ..\i386\cbus1bt.asm \ + ..\i386\cbus2ecc.c \ + ..\i386\cbus.c \ + ..\i386\cbus_sw.c \ + ..\i386\cbusmem.c \ + ..\i386\cbushal.c \ + ..\i386\cbusproc.c + + +DLLDEF=obj\*\hal.def + +MSC_WARNING_LEVEL=/W3 /WX + +!IF $(386) + +NTTARGETFILES=$(TARGETPATH)\i386\halcbusm.lib + +!ENDIF diff --git a/private/ntos/nthals/halcbus/rangesup.c b/private/ntos/nthals/halcbus/rangesup.c new file mode 100644 index 000000000..d9d8db63f --- /dev/null +++ b/private/ntos/nthals/halcbus/rangesup.c @@ -0,0 +1,7 @@ +// +// This file simply includes the common sources from the current HAL +// directory. When the structure is finally changed, the real file should +// be in this directory. +// + +#include "..\rangesup.c" |