517 lines
12 KiB
C
517 lines
12 KiB
C
#ifdef DUO
|
||
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
duosync.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the routines that perform synchronization
|
||
among processors.
|
||
|
||
Author:
|
||
|
||
Lluis Abello (lluis) 06-Apr-1993
|
||
|
||
Environment:
|
||
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "fwp.h"
|
||
#include "iodevice.h"
|
||
#include "led.h"
|
||
#include "selfmap.h"
|
||
#include "selftest.h"
|
||
#include "ioaccess.h"
|
||
#include "fwstring.h"
|
||
|
||
volatile PROCESSOR_B_TASK_VECTOR ProcessorBTask;
|
||
extern BOOLEAN ProcessorBEnabled;
|
||
|
||
VOID
|
||
InitializePCR(
|
||
);
|
||
|
||
|
||
|
||
MEMORY_TEST_DATA BMemTest1 = {
|
||
0xA0100000,
|
||
0x100000,
|
||
0,
|
||
LED_B_MEMORY_TEST_1
|
||
};
|
||
|
||
|
||
MEMORY_TEST_DATA BMemTest2 = {
|
||
0x80200000,
|
||
0x100000,
|
||
0,
|
||
LED_B_MEMORY_TEST_2
|
||
};
|
||
|
||
|
||
MEMORY_TEST_DATA BMemTest3 = {
|
||
VIDEO_MEMORY_VIRTUAL_BASE+1280*1024, // start adr = end of visible screen
|
||
0x200000-1280*1024, // size = rest of video memory.
|
||
0,
|
||
LED_VIDEOMEM
|
||
};
|
||
|
||
//
|
||
// The following table defines the selftest routines that will be executed
|
||
// by processor B.
|
||
//
|
||
|
||
PROCESSOR_B_TEST ProcessorBSelfTests[] = {
|
||
{ProcessorBMemoryTest,&BMemTest1},
|
||
{ProcessorBMemoryTest,&BMemTest2},
|
||
{ProcessorBVideoMemoryTest,&BMemTest3},
|
||
{NULL,0}
|
||
};
|
||
|
||
ULONG
|
||
ExecuteOnProcessorB(
|
||
IN PPROCESSOR_TASK_ROUTINE Routine,
|
||
IN PVOID Data
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine puts the supplied Routine and Data in processor B task vector
|
||
and issues an IP interrupt to processor B which will then execute
|
||
it and set the return value.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PPROCESSOR_B_TASK_VECTOR TaskVector;
|
||
|
||
if (!ProcessorBEnabled) {
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Get a non pointer to the Task Vector
|
||
//
|
||
TaskVector = (PPROCESSOR_B_TASK_VECTOR)&ProcessorBTask;
|
||
|
||
TaskVector->Routine = Routine;
|
||
TaskVector->Data = Data;
|
||
//
|
||
// Issue an IP interrupt to notify processor B that a task has
|
||
// been scheduled for execution.
|
||
//
|
||
WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,2);
|
||
|
||
//
|
||
// If timeout or Return Value indicates error, stop here.
|
||
//
|
||
|
||
if (WaitForIpInterrupt(5000) == FALSE) {
|
||
FwPrint("\r\nTimeout waiting for B to execute %lx\r\n",Routine);
|
||
}
|
||
|
||
return (TaskVector->ReturnValue);
|
||
}
|
||
|
||
|
||
VOID
|
||
ProcessorBMain(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the main routine for processor B.
|
||
It jumps here after initialization.
|
||
The startup sequence is as follows:
|
||
|
||
ProcessorA sets the address of this routine in the ProcessorBTask vector.
|
||
ProcessorA Enables Processor B in the Global Configuration register.
|
||
ProcessorA Calls WaitForIPInterrupt.
|
||
|
||
When Processor B is enabled it runs at the PROM reset vector, it
|
||
initializes itself and jumps to the routine pointed to by ProcessorTask
|
||
which is this routine.
|
||
|
||
Once here processor B Wakes up processor A by issuing an IP interrupt
|
||
and Loops for ever waiting for IP interrupts and executing the Task
|
||
pointed to by ProcessorBTask.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PPROCESSOR_TASK_ROUTINE Task;
|
||
PVOID TaskData;
|
||
ULONG ReturnValue;
|
||
|
||
//
|
||
// Get a pointer to the Task Vector
|
||
//
|
||
|
||
PPROCESSOR_B_TASK_VECTOR TaskVector = (PPROCESSOR_B_TASK_VECTOR)&ProcessorBTask;
|
||
|
||
//
|
||
// Enable IP interrupts.
|
||
// All interrupts are disabled in the psr.
|
||
//
|
||
WRITE_REGISTER_ULONG(&DMA_CONTROL->InterruptEnable.Long,ENABLE_IP_INTERRUPTS);
|
||
|
||
for (;;) {
|
||
|
||
//
|
||
// Wake up processor A
|
||
//
|
||
WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,1);
|
||
|
||
WaitForIpInterrupt(0);
|
||
|
||
//
|
||
// Execute Task
|
||
//
|
||
Task = TaskVector->Routine;
|
||
TaskData = TaskVector->Data;
|
||
ReturnValue = Task(TaskData);
|
||
TaskVector->ReturnValue = ReturnValue;
|
||
}
|
||
}
|
||
|
||
|
||
ULONG
|
||
ProcessorBMemoryTest(
|
||
IN PMEMORY_TEST_DATA MemoryData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tests the portion of memory supplied by Data.
|
||
|
||
Arguments:
|
||
|
||
MemoryData - Pointer to a data structure describing the range of
|
||
memory to be tested.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
PutLedDisplay(MemoryData->LedDisplayValue);
|
||
WriteMemoryAddressTest(MemoryData->StartAddress,MemoryData->Size,MemoryData->XorPattern);
|
||
CheckMemoryAddressTest(MemoryData->StartAddress,MemoryData->Size,MemoryData->XorPattern,MemoryData->LedDisplayValue);
|
||
return 1;
|
||
}
|
||
|
||
|
||
ULONG
|
||
ProcessorBVideoMemoryTest(
|
||
IN PMEMORY_TEST_DATA MemoryData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tests the portion of video memory supplied by Data.
|
||
|
||
Arguments:
|
||
|
||
MemoryData - Pointer to a data structure describing the range of
|
||
memory to be tested.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
PutLedDisplay(MemoryData->LedDisplayValue);
|
||
WriteVideoMemoryAddressTest(MemoryData->StartAddress,MemoryData->Size);
|
||
CheckVideoMemoryAddressTest(MemoryData->StartAddress,MemoryData->Size);
|
||
return 1;
|
||
}
|
||
|
||
ULONG
|
||
CoherencyTest(
|
||
IN PVOID CoherentPage
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs a coherency test. This routine will be executed
|
||
simultaneously by both processors.
|
||
|
||
Processor A stores even bytes and Processor B stores the odd bytes.
|
||
To make sure that cache blocks are passed back and forth, a semaphore
|
||
locks it's access so that the cache line ping pongs from processor to
|
||
processor.
|
||
|
||
|
||
Arguments:
|
||
|
||
CoherentPage. Pointer aligned to a page boundary. Which is marked
|
||
either Exclusive or Shared in the TLB.
|
||
|
||
The first ulong is used as the semaphore. The rest of the page
|
||
is used as data.
|
||
|
||
|
||
Return Value:
|
||
|
||
Number of errors found.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
volatile PULONG Semaphore = (PULONG)CoherentPage;
|
||
PULONG CoherentData = Semaphore+64/sizeof(ULONG);
|
||
ULONG Counter;
|
||
ULONG Processor;
|
||
ULONG DataLong;
|
||
ULONG Errors = 0;
|
||
if (READ_REGISTER_ULONG(&DMA_CONTROL->WhoAmI.Long)) {
|
||
//
|
||
// Processor B
|
||
//
|
||
Processor = 1;
|
||
DataLong = 0xB0B00000;
|
||
} else {
|
||
Processor = 0;
|
||
DataLong = 0xA0A00000;
|
||
}
|
||
|
||
for (Counter = 0; Counter < (0x1000-64)/sizeof(ULONG); Counter += 2) {
|
||
|
||
//
|
||
// Wait for counter.
|
||
// No need for interlocks since each processor waits
|
||
// for a different value.
|
||
//
|
||
|
||
while (*Semaphore != Counter+Processor) {
|
||
}
|
||
*(CoherentData+Processor) = DataLong | Counter;
|
||
*Semaphore = Counter+Processor+1;
|
||
CoherentData += 2;
|
||
}
|
||
|
||
//
|
||
// Both processors check all the data.
|
||
//
|
||
CoherentData = Semaphore+64/sizeof(ULONG);;
|
||
for (Counter = 0; Counter < (0x1000-64)/sizeof(ULONG); Counter +=2) {
|
||
if (*CoherentData != (Counter | 0xA0A00000)) {
|
||
Errors++;
|
||
}
|
||
CoherentData++;
|
||
if (*CoherentData != (Counter | 0xB0B00000)) {
|
||
Errors++;
|
||
}
|
||
CoherentData++;
|
||
}
|
||
return Errors;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
ProcessorBSelftest(
|
||
IN VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the different tasks to be executed by processor
|
||
b and waits for them to complete. This routine is executed by the
|
||
master processor.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE if passed FALSE otherwise
|
||
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN ReturnValue;
|
||
BOOLEAN Timeout;
|
||
|
||
//
|
||
// Get a pointer to the Task Vector
|
||
//
|
||
PPROCESSOR_B_TASK_VECTOR TaskVector = (PPROCESSOR_B_TASK_VECTOR)&ProcessorBTask;
|
||
PPROCESSOR_B_TEST ProcessorBTestList = ProcessorBSelfTests;
|
||
|
||
//
|
||
// Place each task of the SelftestTable in the TaskVector to be executed
|
||
// by processor B.
|
||
//
|
||
while (ProcessorBTestList->Routine != NULL) {
|
||
TaskVector->Routine = ProcessorBTestList->Routine;
|
||
TaskVector->Data = ProcessorBTestList->Data;
|
||
//
|
||
// Issue an IP interrupt to notify processor B that a task has
|
||
// been scheduled for execution.
|
||
//
|
||
WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,2);
|
||
|
||
//
|
||
// If timeout or Return Value indicates error, stop here.
|
||
//
|
||
if (((Timeout = WaitForIpInterrupt(5000)) == FALSE) || (TaskVector->ReturnValue == FALSE)) {
|
||
if (!Timeout) {
|
||
FwPrint("\r\n Wait for Processor B timeout occurred");
|
||
} else {
|
||
FwPrint("\r\n Processor B failed a test");
|
||
}
|
||
FwPrint(" Failed test = %08lx\r\n", (ULONG)ProcessorBTestList->Routine);
|
||
//return FALSE;
|
||
}
|
||
|
||
//
|
||
// Next test.
|
||
//
|
||
ProcessorBTestList++;
|
||
}
|
||
|
||
//
|
||
// Now perform two CoherencyTest
|
||
//
|
||
|
||
FwPrint("\r\n Coherency Test.");
|
||
ReturnValue = TRUE;
|
||
|
||
//
|
||
// Zero the page. Set the Task and notify processor B.
|
||
// And execute the test simultaneously.
|
||
//
|
||
RtlZeroMemory((PVOID)EXCLUSIVE_PAGE_VIRTUAL_BASE,0x1000);
|
||
FwFlushAllCaches();
|
||
TaskVector->Routine = CoherencyTest;
|
||
TaskVector->Data = (PVOID)EXCLUSIVE_PAGE_VIRTUAL_BASE;
|
||
WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,2);
|
||
|
||
if (CoherencyTest((PVOID)EXCLUSIVE_PAGE_VIRTUAL_BASE) != 0) {
|
||
FwPrint(" Processor A Failed.");
|
||
ReturnValue = FALSE;
|
||
} else {
|
||
FwPrint("...");
|
||
}
|
||
|
||
//
|
||
// Get and display results from B
|
||
//
|
||
WaitForIpInterrupt(1000);
|
||
if (TaskVector->ReturnValue != 0) {
|
||
FwPrint(" Processor B Failed.");
|
||
ReturnValue = FALSE;
|
||
} else {
|
||
FwPrint("...");
|
||
}
|
||
|
||
//
|
||
// Do the same with a shared page
|
||
//
|
||
RtlZeroMemory((PVOID)SHARED_PAGE_VIRTUAL_BASE,0x1000);
|
||
FwFlushAllCaches();
|
||
TaskVector->Routine = CoherencyTest;
|
||
TaskVector->Data = (PVOID)SHARED_PAGE_VIRTUAL_BASE;
|
||
WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,2);
|
||
if (CoherencyTest((PVOID)SHARED_PAGE_VIRTUAL_BASE) != 0) {
|
||
FwPrint(" Processor A Failed.");
|
||
ReturnValue = FALSE;
|
||
} else {
|
||
FwPrint("...");
|
||
}
|
||
WaitForIpInterrupt(1000);
|
||
if (TaskVector->ReturnValue != 0) {
|
||
FwPrint(" Processor B Failed.");
|
||
ReturnValue = FALSE;
|
||
} else {
|
||
FwPrint("...");
|
||
}
|
||
|
||
//
|
||
// Make processor B initialize its PCR
|
||
//
|
||
|
||
TaskVector->Routine = (PPROCESSOR_TASK_ROUTINE)InitializePCR;
|
||
TaskVector->Data = 0;
|
||
//
|
||
// Issue an IP interrupt to notify processor B that a task has
|
||
// been scheduled for execution.
|
||
//
|
||
|
||
WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,2);
|
||
|
||
//
|
||
// If timeout or Return Value indicates error, stop here.
|
||
//
|
||
if (WaitForIpInterrupt(5000) == FALSE) {
|
||
// FwPrint("\r\n Wait for Processor B timeout occurred in init PCR\r\n");
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Make processor B cleanup it's caches.
|
||
//
|
||
|
||
TaskVector->Routine = (PPROCESSOR_TASK_ROUTINE)HalSweepDcache;
|
||
TaskVector->Data = 0;
|
||
//
|
||
// Issue an IP interrupt to notify processor B that a task has
|
||
// been scheduled for execution.
|
||
//
|
||
|
||
WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,2);
|
||
|
||
//
|
||
// If timeout or Return Value indicates error, stop here.
|
||
//
|
||
if (WaitForIpInterrupt(5000) == FALSE) {
|
||
// FwPrint("\r\n Wait for Processor B timeout occurred Hal sweep d cachew\r\n");
|
||
return FALSE;
|
||
}
|
||
FwPrint(FW_OK_MSG);
|
||
return ReturnValue;
|
||
}
|
||
|
||
|
||
#endif // DUO
|