/*++ Copyright (c) 1991 Microsoft Corporation Module Name: monitor.c Abstract: This file contains the monitor for the firmware. Author: Lluis Abello (lluis) 09-Sep-91 --*/ #include "fwp.h" #include "monitor.h" #include "selfmap.h" #include "sys\types.h" #include "stdlib.h" #include "string.h" #include "jzsetup.h" #include "selftest.h" #include "fwstring.h" #define BYTE 1 #define HALF 2 #define WORD 4 #define R4000_XCODE_MASK (0x1f << 2) #define XCODE_INSTRUCTION_BUS_ERROR 0x18 #define XCODE_DATA_BUS_ERROR 0x1c VOID RomPutLine( IN PCHAR String ); VOID FillVideoMemory( IN ULONG, IN ULONG, IN ULONG ); #define MoveCursorToColumn(Spaces) \ FwPrint("\r\x9B"#Spaces"C") // // declare static variables. // extern BOOLEAN ProcessorBEnabled; ULONG Argc; ULONG CurrentArg; COMMAND_NAME_ID CurrentCommand; ULONG DataSize; ULONG DataSizeMask; ULONG DataSizeShift; ULONG DefaultAddress; // ****** temp ****** // strtoul sets errno in case of overflow and is not declared anywhere else. // // int errno; PCHAR RegisterNameTable[(REGISTER_NAME_ID)invalidregister] = { "zero", // general register 0 "at", // general register 1 "v0", // general register 2 "v1", // general register 3 "a0", // general register 4 "a1", // general register 5 "a2", // general register 6 "a3", // general register 7 "t0", // general register 8 "t1", // general register 9 "t2", // general register 10 "t3", // general register 11 "t4", // general register 12 "t5", // general register 13 "t6", // general register 14 "t7", // general register 15 "s0", // general register 16 "s1", // general register 17 "s2", // general register 18 "s3", // general register 19 "s4", // general register 20 "s5", // general register 21 "s6", // general register 22 "s7", // general register 23 "t8", // general register 24 "t9", // general register 25 "k0", // general register 26 "k1", // general register 27 "gp", // general register 28 "sp", // general register 29 "s8", // general register 30 "ra", // general register 31 "f0", // fp register 0 "f1", // fp register 1 "f2", // fp register 2 "f3", // fp register 3 "f4", // fp register 4 "f5", // fp register 5 "f6", // fp register 6 "f7", // fp register 7 "f8", // fp register 8 "f9", // fp register 9 "f10", // fp register 10 "f11", // fp register 11 "f12", // fp register 12 "f13", // fp register 13 "f14", // fp register 14 "f15", // fp register 15 "f16", // fp register 16 "f17", // fp register 17 "f18", // fp register 18 "f19", // fp register 19 "f20", // fp register 20 "f21", // fp register 21 "f22", // fp register 22 "f23", // fp register 23 "f24", // fp register 24 "f25", // fp register 25 "f26", // fp register 26 "f27", // fp register 27 "f28", // fp register 28 "f29", // fp register 29 "f30", // fp register 30 "f31", // fp register 31 "fsr", // fp status register "index", // cop0 register 0 "random", // cop0 register 1 "entrylo0", // cop0 register 2 "entrylo1", // cop0 register 3 "context", // cop0 register 4 "pagemask", // cop0 register 5 "wired", // cop0 register 6 "badvaddr", // cop0 register 8 "count", // cop0 register 9 "entryhi", // cop0 register 10 "compare", // cop0 register 11 "psr", // cop0 register 12 "cause", // cop0 register 13 "epc", // cop0 register 14 "prid", // cop0 register 15 "config", // cop0 register 16 "lladdr", // cop0 register 17 "watchlo", // cop0 register 18 "watchhi", // cop0 register 19 "ecc", // cop0 register 26 "cacheerror", // cop0 register 27 "taglo", // cop0 register 28 "taghi", // cop0 register 29 "errorepc" // cop0 register 30 }; CHAR UnknownException[] = "Unknown"; PCHAR ExceptionNameTable[32] = { "Int", // exception code 0 "Mod", // exception code 1 "TlbL", // exception code 2 "TlbS", // exception code 3 "AdEL", // exception code 4 "AdES", // exception code 5 "IBE", // exception code 6 "DBE", // exception code 7 "Sys", // exception code 8 "Bp", // exception code 9 "RI", // exception code 10 "CpU", // exception code 11 "Ov", // exception code 12 "Tr", // exception code 13 "VCEI", // exception code 14 "FPE", // exception code 15 UnknownException, // exception code 16 UnknownException, // exception code 17 UnknownException, // exception code 18 UnknownException, // exception code 19 UnknownException, // exception code 20 UnknownException, // exception code 21 UnknownException, // exception code 22 "WATCH", // exception code 23 UnknownException, // exception code 24 UnknownException, // exception code 25 UnknownException, // exception code 26 UnknownException, // exception code 27 UnknownException, // exception code 28 UnknownException, // exception code 29 UnknownException, // exception code 30 "VCED" // exception code 31 }; PCHAR CommandNameTable[(COMMAND_NAME_ID)invalidcommand] = { "d", "db", "dw", "dd", "e", "eb", "ew", "ed", "o", "ob", "ow", "od", "i", "ib", "iw", "id", "r", "z", "f", "a", "h", "?", #ifdef DUO "s", #endif "q" }; extern ULONG RegisterTable[(REGISTER_NAME_ID)invalidregister]; extern LONG FwRow; extern LONG FwColumn; REGISTER_NAME_ID GetRegister( IN PCHAR RegName ) /*++ Routine Description: This routine returns the index in the register table for the given register. Or invalid if the given register name doesn't match any register. Arguments: RegName - Null terminated string that contains the name of the register. Return Value: Index on the Register Table for the requested register. --*/ { REGISTER_NAME_ID RegId; for (RegId = 0; RegId < MON_INVALID_REGISTER_MSG; RegId++) { if (strcmp(RegisterNameTable[RegId],RegName) == 0) { break; } } return RegId; } ULONG StrToUlong( IN PCHAR String, OUT CHAR ** Terminator ) /*++ Routine Description: This routine converts an ascii string to an unsigned long Arguments: String - Null terminated string that contains the value to convert. Terminator - Address of a pointer to a character. This routine sets it to point to the cahracter in String that terminated the conversion. This is '\0' in a normal case or whatever garbage the string contained. Return Value: Returns the converted string --*/ { ULONG Ulong = 0; ULONG Index; for (Index=0;String[Index]; Index++) { if ((String[Index] >= '0') && (String[Index] <= '9')) { Ulong <<= 4; Ulong |= String[Index] - '0'; continue; } // // Strings are always lower case so this is not needed. // //if ((String[Index] >= 'A') && (String[Index] <= 'F')) { // Ulong <<= 4; // Ulong |= String[Index] - 'A' + 10; // continue; //} if ((String[Index] >= 'a') && (String[Index] <= 'f')) { Ulong <<= 4; Ulong |= String[Index] - 'a' + 10; continue; } *Terminator = &String[Index]; return 0xFFFFFFF; } // // Check for overflow // if (Index > 8) { *Terminator = &String[0]; } else { *Terminator = &String[Index]; } return Ulong; } BOOLEAN GetAddress( IN PUCHAR String, OUT PULONG Address ) /*++ Routine Description: This routine converts an ascii string to an address. The string is the form: [@reg | value] Arguments: String - Null terminated string that contains the address to convert. Address - Supplies a pointer to where the converted address is stored Return Value: Returns TRUE if the string can be converted. FALSE otherwise. --*/ { PUCHAR Terminator; UCHAR Delimiter; REGISTER_NAME_ID RegId; if (*String == '@') { String++; // skip @ if ((RegId = GetRegister(String)) == MON_INVALID_REGISTER_MSG) { FwPrint(String); FwPrint(MON_INVALID_REGISTER_MSG); return FALSE; } else { *Address = RegisterTable[RegId]; } } else { *Address=StrToUlong(String,&Terminator); if (*Terminator != '\0') { // // Not the whole string was converted. // FwPrint(Terminator); FwPrint(MON_NOT_VALID_ADDRESS_MSG); return FALSE; } } return TRUE; } BOOLEAN GetAddressRange( IN PCHAR Argv[], OUT PULONG StartAddress, OUT PULONG EndAddress ) /*++ Routine Description: This routine converts an ascii string to a range, returning the starting and end address. The syntax for an address range is one of the following startaddress endaddres startaddress l numberofelements Arguments: Argv - array of zero terminated argument strings. StartAddress - Supplies a pointer to where the Start address is stored EndAddress - Supplies a pointer to where the End address is stored Return Value: Returns TRUE if Argv specifies a valid address range. FALSE otherwise. --*/ { PCHAR Tmp; CHAR Delimiter; if (CurrentArg == Argc) { return; } if (GetAddress(Argv[CurrentArg],StartAddress) == FALSE) { return FALSE; } CurrentArg++; if (CurrentArg == Argc) { *EndAddress = *StartAddress+128; return TRUE; } if (strcmp(Argv[CurrentArg],"l") == 0 ) { // // this is a range of the form "startaddress l count" // CurrentArg++; if (CurrentArg == Argc) { FwPrint(MON_INVALID_ARGUMENT_COUNT_MSG); return FALSE; } if (GetAddress(Argv[CurrentArg],EndAddress) == FALSE) { return FALSE; } CurrentArg++; *EndAddress = (*EndAddress< Start // if (*EndAddress <= *StartAddress) { FwPrint(MON_INVALID_ADDRESS_RANGE_MSG); return FALSE; } return TRUE; } COMMAND_NAME_ID GetCommand( IN PCHAR CommandName ) /*++ Routine Description: This routine tries to match the supplied string with one of the recognized commands. Arguments: CommandName - Supplies a string to be matched with one of the monitor commands. Return Value: Returns a value that identifies the command. --*/ { COMMAND_NAME_ID Index; for (Index=0;Index 16) { LineLength = 16; } for (i=0;iDevicePath != NULL) { FwPrint(PLookupTable->DevicePath); FwPrint(FW_CRLF_MSG); PLookupTable++; } return(TRUE); } VOID Monitor( IN ULONG CallerSource ) /*++ Routine Description: This is the main dispatch routine to the various commands that can be typed at the monitor prompt. Arguments: CallerSource - 0 if a UTLB or General exception occurred. - 1 if an NMI_EXCEPTION occurred. - 2 if a CACHE_EXCEPTION occurred. - 3 if the caller is not an exception handler. Return Value: None. --*/ { CHAR Buffer[2][128]; ULONG ParityDiag[3]; PULONG ParDiag; ULONG BufferIndex; PCHAR Argv[10]; PCHAR Tmp; CHAR String[128]; BOOLEAN CommandValid; GETSTRING_ACTION Action; ULONG Index; JzSetScreenAttributes( TRUE, FALSE, FALSE); #ifdef DUO // // Set text White for processor A. Yellow for processor B // if (READ_REGISTER_ULONG(&DMA_CONTROL->WhoAmI.Long) == 0) { JzSetScreenColor(ArcColorWhite, ArcColorBlue); } else { JzSetScreenColor(ArcColorYellow, ArcColorBlue); } #else JzSetScreenColor(ArcColorWhite, ArcColorBlue); #endif FwPrint(MON_JAZZ_MONITOR_MSG); sprintf(String,"%ld\r\n",*(PULONG)(PROM_ENTRY(2))); FwPrint(String); FwPrint(MON_PRESS_H_MSG); // // Initialize command line to null. // Argv[0] = (PCHAR)NULL; if (CallerSource !=3) { // // Display Cause of exception. // switch(CallerSource) { case 0: FwPrint(ExceptionNameTable[((RegisterTable[cause])&0xFF) >> 2]); break; case NMI_EXCEPTION: FwPrint(MON_NMI_MSG); break; case CACHE_EXCEPTION: FwPrint(MON_CACHE_ERROR_MSG); break; } FwPrint(MON_EXCEPTION_MSG); // // simulate a dump all registers command; // Argc = 1; RegisterCommand(Argv); Argc = 0; } // // Initialize Static variables. // DefaultAddress = KSEG1_BASE; DataSize = WORD; BufferIndex = 0; #ifdef DUO // // Report system parity exceptions. // // if ((((RegisterTable[cause]&R4000_XCODE_MASK) == XCODE_DATA_BUS_ERROR) || ((RegisterTable[cause]&R4000_XCODE_MASK) == XCODE_INSTRUCTION_BUS_ERROR)) && (READ_REGISTER_ULONG(&DMA_CONTROL->NmiSource.Long) == 1)) { // // This is a bus error exception. And the cause is ecc error. // sprintf(String, MON_ECC_ERROR_MSG, RegisterTable[epc] ); FwPrint(String); // // align ParDiag to a double word address // ParDiag = (PULONG) ((ULONG)(&ParityDiag[1]) & 0xFFFFFFF8); LoadDoubleWord(&DMA_CONTROL->EccDiagnostic,ParDiag); sprintf(String, MON_MEM_ECC_FAILED_MSG, READ_REGISTER_ULONG(&DMA_CONTROL->MemoryFailedAddress.Long), *ParDiag, *(ParDiag+1) ); } #endif if (CallerSource == CACHE_EXCEPTION) { // // try to print as much info as possible. // sprintf(String, MON_CACHE_ERROR_EPC_MSG, RegisterTable[cacheerror], RegisterTable[errorepc] ); FwPrint(String); #ifndef DUO // // Do not print Parity Diag for a DUO CacheError exception since it's only // an internal one. // // // align ParDiag to a double word address // ParDiag = (PULONG) ((ULONG)(&ParityDiag[1]) & 0xFFFFFFF8); LoadDoubleWord(&DMA_CONTROL->ParityDiagnosticLow.Long,ParDiag); sprintf(String, MON_PARITY_DIAG_MSG, *ParDiag, *(ParDiag+1) ); FwPrint(String); #endif // // Now we will probably die, as the keyboard is interrupt // driven and the interrupt handler is cached... Life is Tough! // } // // loop forever getting commands and dispatching them // while(TRUE) { // // print prompt // FwPrint(">"); // // read a command. // do { Action = FwGetString(Buffer[BufferIndex],128,NULL,FwRow,FwColumn); } while ((Action != GetStringSuccess) && (Action != GetStringEscape)); FwPrint(FW_CRLF_MSG); // // convert string to lower case. // for(Tmp=Buffer[BufferIndex];*Tmp;*Tmp++) { *Tmp=tolower(*Tmp); } // // if escape was pressed, simulate a quit command. // if (Action == GetStringEscape) { Argc = 1; Argv[0] = "q"; // // separate command line into tokens delimited by spaces // load up Argv with pointers to arguments and put count in Argc // } else if (*Buffer[BufferIndex] != '\0') { Tmp = Buffer[BufferIndex]; Argc = 0; // // Skip leading blanks // while ( *Tmp == ' ') { Tmp++; } while ( *Tmp ) { Argv[Argc++] = Tmp; while ( *Tmp ) { if (*Tmp == ' ') { *Tmp++ = '\0'; while ( *Tmp == ' ') { Tmp++; } break; } Tmp++; } } //if ((Argv[Argc] = strtok(Buffer[BufferIndex]," ")) != NULL) { // Argc++; // while((Argv[Argc]=strtok((PCHAR)NULL," ")) != NULL) { // Argc++; // } //} // // Increment index so that next command is read into the other // buffer. And we preserve the previous one. // BufferIndex = (BufferIndex+1) & 0x1; } else { // // repeat the last command already in Argv Argc // } // // if first argument is not null, then dispatch to routines. // if (Argv[0] != (PCHAR) NULL) { CurrentArg = 1; CurrentCommand = GetCommand(Argv[0]); switch(CurrentCommand) { case DumpByte: case DumpWord: case DumpDouble: DataSizeShift = (CurrentCommand - Dump -1); DataSize = 1 << DataSizeShift; DataSizeMask = DataSize-1; case Dump: CommandValid = DumpCommand(Argv); break; case EnterByte: case EnterWord: case EnterDouble: DataSizeShift = (CurrentCommand - Enter -1); DataSize = 1 << DataSizeShift; DataSizeMask = DataSize-1; case Enter: CommandValid = EnterCommand(Argv); break; case OutputByte: case OutputWord: case OutputDouble: DataSizeShift = (CurrentCommand - Output -1); DataSize = 1 << DataSizeShift; DataSizeMask = DataSize-1; case Output: CommandValid = OutputCommand(Argv); break; case InputByte: case InputWord: case InputDouble: DataSizeShift = (CurrentCommand - Input -1); DataSize = 1 << DataSizeShift; DataSizeMask = DataSize-1; case Input: CommandValid = InputCommand(Argv); break; case Register: CommandValid = RegisterCommand(Argv); break; case Zero: CommandValid = ZeroCommand(Argv); break; case Fill: CommandValid = FillCommand(Argv); break; case AvailableDevices: CommandValid = FwDumpLookupTable(Argv); break; case Help: case Help2: for (Index = 0 ; Index < MON_HELP_SIZE ; Index++) { FwPrint(MON_HELP_TABLE[Index]); FwPrint(FW_CRLF_MSG); } break; #ifdef DUO case SwitchProcessor: if (ProcessorBEnabled == FALSE) { FwPrint(MON_PROCESSOR_B_MSG); break; } if (READ_REGISTER_ULONG(&DMA_CONTROL->WhoAmI.Long) == 0) { PPROCESSOR_B_TASK_VECTOR TaskVector; // // Set the monitor entry point in the TaskVector // TaskVector = (PPROCESSOR_B_TASK_VECTOR)&ProcessorBTask; TaskVector->Routine = FwMonitor; TaskVector->Data = 0; WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,2); WaitForIpInterrupt(0); JzSetScreenColor(ArcColorWhite, ArcColorBlue); break; } // // if processor B then do the same as in a quit command. // #endif case Quit: if (CallerSource == 3) { return; } else { // // We came because of an exception. // The only way to exit is reseting the system. // FwPrint(MON_NO_RETURN_MSG); FwPrint(MON_RESET_MACHINE_MSG); do { Action = FwGetString(Buffer[BufferIndex],128,NULL,FwRow,FwColumn); } while ((Action != GetStringSuccess) && (Action != GetStringEscape)); FwPrint(FW_CRLF_MSG); Buffer[BufferIndex][0]=tolower(Buffer[BufferIndex][0]); if (strcmp(Buffer[BufferIndex],"y") == 0) { ResetSystem(); // ArcReboot(); } break; } case invalidcommand: FwPrint(MON_UNRECOGNIZED_COMMAND_MSG); // // Clear the argument so that re-do last command // doesn't repeat the erroneous command. // CommandValid = FALSE; break; } if (!CommandValid) { Argv[0] = (PCHAR) NULL; } } } }