summaryrefslogtreecommitdiffstats
path: root/private/ntos/nthals/halcbus
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/nthals/halcbus
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to '')
-rw-r--r--private/ntos/nthals/halcbus/bushnd.c7
-rw-r--r--private/ntos/nthals/halcbus/dirs24
-rw-r--r--private/ntos/nthals/halcbus/drivesup.c7
-rw-r--r--private/ntos/nthals/halcbus/eisa/makefile6
-rw-r--r--private/ntos/nthals/halcbus/eisa/makefile.inc2
-rw-r--r--private/ntos/nthals/halcbus/eisa/sources112
-rw-r--r--private/ntos/nthals/halcbus/hal.rc11
-rw-r--r--private/ntos/nthals/halcbus/i386/cb1stall.asm932
-rw-r--r--private/ntos/nthals/halcbus/i386/cb2stall.asm1347
-rw-r--r--private/ntos/nthals/halcbus/i386/cbapic.c898
-rw-r--r--private/ntos/nthals/halcbus/i386/cbdetect.c545
-rw-r--r--private/ntos/nthals/halcbus/i386/cbdriver.c366
-rw-r--r--private/ntos/nthals/halcbus/i386/cbioacc.asm561
-rw-r--r--private/ntos/nthals/halcbus/i386/cbmapint.c579
-rw-r--r--private/ntos/nthals/halcbus/i386/cbswint.asm444
-rw-r--r--private/ntos/nthals/halcbus/i386/cbsysint.asm590
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus.c1281
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus.h97
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus.inc277
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus1.c1870
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus1.h70
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus1bt.asm314
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus2.c4170
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus2.h397
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus2cbc.asm283
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus2ecc.c475
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus_nt.h226
-rw-r--r--private/ntos/nthals/halcbus/i386/cbus_sw.c191
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusapic.asm715
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusapic.h256
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusboot.asm362
-rw-r--r--private/ntos/nthals/halcbus/i386/cbushal.c482
-rw-r--r--private/ntos/nthals/halcbus/i386/cbuslock.asm512
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusmem.c294
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusmisc.asm406
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusnls.h41
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusnmi.c204
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusproc.c427
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusprof.asm193
-rw-r--r--private/ntos/nthals/halcbus/i386/cbusrrd.h299
-rw-r--r--private/ntos/nthals/halcbus/i386/halp.h5
-rw-r--r--private/ntos/nthals/halcbus/i386/ix8259.inc5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixbeep.asm5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixbusdat.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixcmos.asm5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixcmos.inc5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixdat.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixenvirv.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixfirm.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixhwsup.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixidle.asm5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixinfo.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixisa.h5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixisabus.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixisasup.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixkdcom.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixkdcom.h5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixmca.h5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixmcabus.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixmcasup.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixpcibrd.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixpcibus.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixpciint.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixphwsup.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixprofil.asm5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixreboot.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixthunk.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/ixusage.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/pcip.h5
-rw-r--r--private/ntos/nthals/halcbus/i386/xxbiosa.asm5
-rw-r--r--private/ntos/nthals/halcbus/i386/xxbiosc.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/xxdisp.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/xxflshbf.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/xxkdsup.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/xxmemory.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/xxstubs.c5
-rw-r--r--private/ntos/nthals/halcbus/i386/xxtime.c5
-rw-r--r--private/ntos/nthals/halcbus/mca/makefile6
-rw-r--r--private/ntos/nthals/halcbus/mca/makefile.inc2
-rw-r--r--private/ntos/nthals/halcbus/mca/sources116
-rw-r--r--private/ntos/nthals/halcbus/rangesup.c7
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"