/* Copyright Motorola, Inc. 1993, 1994 ALL RIGHTS RESERVED You are hereby granted a copyright license to use, modify, and distribute the SOFTWARE so long as this entire notice is retained without alteration in any modified and/or redistributed versions, and that such modified versions are clearly identified as such. No licenses are granted by implication, estoppel or otherwise under any patents or trademarks of Motorola, Inc. The SOFTWARE is provided on an "AS IS" basis and without warranty. To the maximum extent permitted by applicable law, MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED, INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE AND ANY WARRANTY AGAINST INFRINGEMENT WITH REGARD TO THE SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF) AND ANY ACCOMPANYING WRITTEN MATERIALS. To the maximum extent permitted by applicable law, IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE SOFTWARE. Motorola assumes no responsibility for the maintenance and support of the SOFTWARE. */ /************************************************************************* file: except1.c called from: except2.s functions: deal_w_exception -- tells the user what exception has occurred input: ex_type -- the value that determines the exception type; it is set in the exception vector routines output: error message containing exception type return: none The exception vector offset information was taken from the PowerPC 601 User's Manual, Rev 1, Ch. 5, Table 5-2 on pages 5-3 to 5-5. history: 12/31/97 - subtle changes to how exceptions are handled and reported - RGP 05/22/97 - ported to MetaWare 02/10/93 - created - JAF **************************************************************************/ #include "config.h" #include "cpu.h" #include "toks.h" #include "errors.h" #include "dink.h" #include "reg_tb.h" #define TRUE 1 extern long ex_type; extern long ex_addr; extern long usr_code_rtn; extern STATUS is_char_in_duart(); extern STATUS is_a_breakpoint(); extern STATUS restore_breakpoints(); extern STATUS special_register_read(); extern STATUS special_register_write(); extern STATUS read_from_memory(); extern STATUS invalidate_and_enable_L1_icache(); extern STATUS dink_loop(); extern STATUS disassemble_an_opcode(); extern STATUS invalidate_and_enable_L1_dcache(); extern int flush_invalidate_disable_L1caches(); extern int flush_caches(); INTEGER value; #ifdef MMU_FLOATING_BATS extern long in_which_code; void change_floating_bats() { unsigned long batu,batl; unsigned long dar = 0; extern char process_type; switch(in_which_code) { case 0: dar = special_register_file[DAR_LOC].dink; break; case 1: dar = special_register_file[DAR_LOC].user; break; }; /* this sets the BLPI to where the excption happened */ batu = dar & 0xfffc0000; /* this allows all access to that area but it is CI/WT */ batu |= 0x0e; /* we will always remap it to low memory ram and it is 8Meg. */ batl = 0x7f; if (process_type != PPC601) switch(in_which_code) { case 0: special_register_file[IBAT3U_LOC].dink = batu; special_register_file[IBAT3L_LOC].dink = batl; special_register_file[DBAT3U_LOC].dink = batu; special_register_file[DBAT3L_LOC].dink = batl; break; case 1: special_register_file[IBAT3U_LOC].user = batu; special_register_file[IBAT3L_LOC].user = batl; special_register_file[DBAT3U_LOC].user = batu; special_register_file[DBAT3L_LOC].user = batl; break; }; } #endif /* MMU_FLOATING_BATS */ long deal_w_exception() { char *str_type; long return_value; extern char process_type; extern INTEGER benchmark_in_progress; /* given the exception type value, determine which exception has occurred and assign a string to the type variable */ return_value = 0; switch (ex_type) { case 0x200: /* 00200 */ str_type = "A Machine Check"; break; case 0x300: /* 00300 */ str_type = "A Data Access"; #ifdef MMU_FLOATING_BATS change_floating_bats(); #endif break; case 0x400: /* 00400 */ str_type = "An Instruction Access"; #ifdef MMU_FLOATING_BATS change_floating_bats(); #endif break; case 0x500: /* 00500 */ return_value = is_char_in_duart() ; if( return_value == 0) { str_type = "An External Interrupt"; } else { #ifdef INTERRUPT_DRIVEN return_value = 0; mader_get_char_for_int(); str_type = (char *) 0; #else str_type = (char *) 0; return_value = 1; #endif } break; case 0x600: /* 00600 */ str_type = "An Alignment"; break; case 0x700: /* 00700 */ if(( is_a_breakpoint(ex_addr) == SUCCESS) || (benchmark_in_progress == TRUE)) str_type = (char *) 0; else str_type = "A Program"; break; case 0x800: /* 00800 */ str_type = "A Floating-point Unavailable"; break; case 0x900: /* 00900 */ str_type = "A Decrementer"; break; case 0xa00: /* 00A00 */ if (process_type == PPC601) str_type = "An I/O Error"; else str_type = "An Unidentifiable"; break; case 0xc00: /* 00C00 */ str_type = "A System Call"; break; case 0xd00: /* 00D00 */ str_type = "A Trace"; break; case 0xe00: /* 00E00 */ if (process_type == PPC604) str_type = "A Floating-Point Assist"; else str_type = "An Unidentifiable"; break; case 0xf20: /* 00F20 */ if (process_type == PPCMAX) str_type = "VMX Unavailable"; else str_type = "An Unidentifiable"; break; case 0x1000: /* 01000 */ if (process_type == PPC603) str_type = "An Instruction Translation Miss"; else str_type = "An Unidentifiable"; break; case 0x1100: /* 01100 */ if (process_type == PPC603) str_type = "A Data Load Translation Miss"; else str_type = "An Unidentifiable"; break; case 0x1200: /* 01200 */ if (process_type == PPC603) str_type = "A Data Store Translation Miss"; else str_type = "An Unidentifiable"; break; case 0x1300: /* 01300 */ if (process_type == PPC601) str_type = "An Unidentifiable"; else str_type = "An Instruction Address Breakpoint"; break; case 0x1400: /* 01400 */ if (process_type == PPC601) str_type = "An Unidentifiable"; else str_type = "A System Management Interrupt"; break; case 0xF00: /* 00F00 */ if ((process_type == PPCART) || (process_type == PPC604) || (process_type == PPC755) || (process_type == PPCMAX)) str_type = "A Performance Monitoring Interrupt"; else str_type = "An Unidentifiable"; break; case 0x1600: /* 01600 */ if (process_type == PPCMAX) str_type = "VMX Assist - Java mode denorm detection"; else str_type = "An Unidentifiable"; break; case 0x1700: /* 01700 */ if ((process_type == PPCART) || (process_type == PPC755) || (process_type == PPCMAX)) str_type = "Thermal Management Interrupt"; else str_type = "An Unidentifiable"; break; case 0x2000: /* 02000 */ if (process_type == PPC601) str_type = "A Run Mode or Trace"; else str_type = "An Unidentifiable"; break; /* Added case for return from user code via blr, this is not a valid exception vector */ case 0xAAAA: /* blr from user code */ str_type = "A Return From User Code"; break; default: str_type = "An Unidentifiable"; break; } /* now print to the user what type of exception has occurred */ /* tpeters 03/09/00 modified the following if statement to not only check if exception was due to an breakpoint exception, but now to also check if the exception handler was invoked for a safe return from user code using the blr command. */ if ((str_type != (char *) 0) && (!usr_code_rtn)) { PRINT( "%s exception ", str_type ); PRINT( "0x%08lx has occurred.\n", ex_type ); } else if (usr_code_rtn) { PRINT( "Successful return to DINK from user code!\n"); } return return_value; } /* 1 2 3 4 5 6 7 8 01234567890123456789012345678901234567890123456789012345678901234567890123456789 function: exception_return purpose: inputs: */ exception_return() { /* I'll use status to tell me if the functions I call did what I want them to do. */ STATUS status; /* This will be the opcode we will disassemble at the address that the user's code stopped executing. */ unsigned long opcode; /* This variable is used for special purpose register reads. */ INTEGER value; extern STATUS mmu_setup(); extern STATUS global_L2backside_invalidate(); extern STATUS init_L2backside_cache(); extern bench_mark(); extern INTEGER benchmark_in_progress; extern INTEGER benchmark_pass; /* Now we get to do a little cleanup. All exceptions are going to return through this routine. DINK's stack better still be working at this point or after we handle printing things out we are going to choke trying to get to DINK's command prompt. We'll worry about that later. */ /* RGP -- 1/2/98 */ /* We need to also make sure we are NOT in the middle of a benchmark pass */ if(benchmark_in_progress == TRUE) { benchmark_pass++; bench_mark(); } /* Now we need to get our original opcodes back out into memory so the user won't see the illegal opcodes we've put out there. */ restore_breakpoints(); /* Flush out all our modified instructions from the caches so our i-cache will be coherent with our d-cache. */ /* RGP - We may not want to disable the caches here. The restore to DINK should have set the proper registers into the processor. We may just want to flush the caches so that the User's changes are placed back into memory. I may not even have to worry about even flushing since HW enforced coherency should take care of this. */ flush_invalidate_disable_L1caches(); /* We'll check to see if the the User code encountered one of their breakpoints. To do that, all we have to do is check the address in the user's copy of SRR0 and see if it's in the breakpoint data structure. */ special_register_read(SRR0_LOC,&value); if( is_a_breakpoint( value ) == SUCCESS ) { /* Okay, the user did in fact hit a breakpoint. I need to let him know that. */ PRINT( "Breakpoint Encountered:\n" ); } /* Restore MSR without trace bit set. */ special_register_read(SRR1_LOC,&value); special_register_write(SRR1_LOC,(value & 0xFFFFFBFF)); /* We'll check to see if the the User code returned to DINK via blr not via an exception, althought the route is the same for these two methods (through the exception handler). We will use the global: usr_code_rtn != 0 to tell us user code returned to DINK safely via blr, else user code returned via an exception or breakpoint. If the return was due to a blr, then we don't need to disassemble anything. tpeters 03/09/00 */ if(!usr_code_rtn){ /* Let the user know where his execution ended. */ special_register_read(SRR0_LOC,&value); PRINT( "Current Instruction Pointer: 0x%08lx ",value); /* We will abstract the read from memory by calling a function called "read_from_memory". I don't know how it does it, but it will put the data from memory into my "opcode" variable. */ special_register_read(SRR0_LOC,&value); status = read_from_memory( value, &opcode); /* Call something that will disassemble the 32-bit opcode just read from memory and will also print it out. NOTE: the function "disassemble_an_opcode" will NOT print a '\n' after it prints out the mnemonic. It's up to me or someone else to print that '\n'. This is good because I don't want a '\n' printed. */ #ifdef ASM_DSM_AVAILABLE disassemble_an_opcode( opcode, value); #endif /* The correct mnemonic has been printed out. Now I can finish off the line with '\n'. */ PRINT("\n"); } //if(!usr_code_rtn) usr_code_rtn = 0; //clear the global /* Let's try to get the caches back on if they were on when we left */ #ifdef L2_BACKSIDE_ON global_L2backside_invalidate(); #endif #ifdef ICACHEON invalidate_and_enable_L1_icache(); #endif #ifdef DCACHEON flush_caches(); invalidate_and_enable_L1_dcache(); #endif #ifdef L2_BACKSIDE_ON init_L2backside_cache(); #endif dink_loop(); /* I want back to the command prompt ... */ } /* end exception_return */