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
|