#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