/* $Id: go_tr1.c,v 1.15 1999/10/30 01:41:11 ecueva Exp $ Copyright Motorola, Inc. 1993, 1994,1999 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. */ #include "dink.h" #include "errors.h" #include "reg_tb.h" #include "toks.h" #include "spr_loc.h" #include #define TRUE 1 /*--------------------------------------------------------------------- ===================== Prototype Our Functions ======================= --------------------------------------------------------------------- */ extern STATUS invalidate_and_enable_L1_icache(); extern STATUS invalidate_and_enable_L1_dcache(); extern STATUS disable_L1_dcache(); extern STATUS disable_L1_icache(); extern cache_inhibit(); extern STATUS special_register_write(); extern STATUS special_register_read(); extern STATUS icache_global_invalidate(); extern STATUS go_into_guts(); extern STATUS is_a_breakpoint(); extern STATUS flush_breakpoints(); extern STATUS getarg_adr(); extern void fL1dc(); extern flush_dcache_max(); extern char process_type; STATUS go_trace(); /* 1 2 3 4 5 6 7 8 01234567890123456789012345678901234567890123456789012345678901234567890123456789 function: go_trace purpose: This is a dual-purpose function (the best kind...). We will either execute the user's command to "go" or the user's command to "trace". Either way, we will transfer CPU control over to the user's instruction stream. "Go" will simply give control over to the user's instruction stream and we won't see it again until we hit a break point. "Trace" will only let the user execute a single instruction from his stream and control will then return to us. inputs: Three parameters are sent in: start_addr: This is the starting address in memory where we will begin execution. is_valid_addr: This flag will tell us if the data sent in through "start_addr" is valid. It may not because the user may have given the "+" option, in which case, we will assume that the adress in SRR0 is already set to where we want it. The "+" option may be a valid possibility for either the trace or go commands. 0 indicates the address is NOT valid. 1 indicates the address is valid. is_trace: This flag tells me whether this is a trace command or a go command. outputs: None. return: I will return a SUCCESS if everything went okay and some other inocuous error if something went astray. 12/31/97 Extensive rewrite to correct go/trace functionality --- RGP mod history: 03/17/93 Created --- MH */ STATUS go_trace( start_addr, is_valid_addr, is_trace ) /* Please refer to the header information for descriptions of these parameters. */ long start_addr; char is_valid_addr; char is_trace; { /* This variable is used for special purpose register reads. */ INTEGER value; if( is_valid_addr == 1 ) { /* The caller is asking us to go run some code at the address that was passed in. In order to do that, we need to put that address in the user's programming model data structure in what he thinks is SRR0 */ /* Before we do that, we need to make sure the bottom two bits are clear in the address we are about to put in SRR0 */ start_addr = start_addr & 0xFFFFFFFC; /* Write the address into what the user thinks is SRR0. */ special_register_write(SRR0_LOC,start_addr); } /* Once we get here, one way or another, we have SRR0 updated properly. */ /* Now we will check to see if we are going to be doing a trace or a go. */ /* Flush the breakpoints out to memory so regardless of whether we are performing a go or trace we will find the illegal opcode out in memory */ flush_breakpoints(); if( is_trace == 1 ) { /* TRACE IS BEING PERFORMED */ /* If we get here, then we have to make sure that the "SE" bit is set in the user's copy of SRR1 so that only one instruction will be executed. */ special_register_read(MSR_LOC,&value); special_register_write(MSR_LOC, (value | 0x00000400)); /* Note: The low level restore_spr routine places the MSR into SRR1 before the RFI */ /* Flush out all our modified instructions from the caches so our i-cache will be coherent with our d-cache. */ flush_invalidate_disable_L1caches(); /* Now we trace a single instruction! We should return thru this code after executing the trace. The go_into_guts routine actually launches us into user code. Somehow we must return here. That somehow is to return to the global exception_return label below. */ go_into_guts(); } else { /* GO IS BEING PERFORMED */ /* If we get here, then we are *NOT* doing a trace. There are a couple of things I need to do in this situation. Therefore, it must be a GO. */ /* Make sure the user doesn't have the trace bit set */ special_register_read(MSR_LOC,&value); special_register_write(MSR_LOC,(value & 0xFFFFFBFF)); /* Note: The low level restore_spr routine places the MSR into SRR1 before the RFI */ /* Flush out all our modified instructions from the caches so our i-cache will be coherent with our d-cache. */ flush_invalidate_disable_L1caches(); /* Let the user have control until some exception (breakpoint) occurs. */ go_into_guts(); } return(1); /* make the compiler happy about return statements */ } /* end go_trace */ /* ========================================================== These conditionally compiled functions provide stubs here that would ordinarily be written in native assembly. ========================================================== */ flush_invalidate_disable_L1caches() { /* This crude function flushes the cache by reading and writing 512K/8 of data. */ fL1dc((1024 * 512)/8); icache_global_invalidate(); invalidate_and_enable_L1_icache(); disable_L1_icache(); invalidate_and_enable_L1_dcache(); disable_L1_dcache(); } flush_caches() { /* This crude function flushes the cache by reading and writing 512K of data. */ long i; if (process_type == PPCMAX) /* Only Max uses the special dl1HWF bit in the MSSCR0 register, * other chips use the dcbf instruction */ { flush_dcache_max(); icache_global_invalidate(); return SUCCESS; } /* otherwise this is not a MAX chip */ #ifdef GCC asm("lis %r4,0x0"); #else _ASM("lis %r4,0x0"); #endif /*GCC*/ /*This function was getting the writes optimized out, and I got worried about overwriting data space, so I just changed it to a bunch of reads to memory. Who cares if I read the memory, as long as I don't modify it, eh? AMSD 8/6/97*/ for( i = 0; i < ((1024 * 512)/8); i++ ) { #ifdef GCC asm("dcbf %r0,%r4"); asm("addi %r4,%r4,32"); #else _ASM("dcbf %r0,%r4"); _ASM("addi %r4,%r4,32"); #endif /* GCC */ } /* If we are going to flush the caches, we need to flush icache too. AMSD 8/6/97 */ icache_global_invalidate(); } #ifndef ON_BOARD go_into_guts() { } #endif STATUS par_go_trace(go_or_trace) int go_or_trace; { #ifdef ON_BOARD INTEGER adr; /* temp. address for command parameter */ STATUS status; /* temporary status variable */ int valid_address; /* to pass to the go_trace() routine */ /* lets get the compiler to shut up here */ valid_address =0; adr = 0; /* lets get the arguments needed */ /* first lets see if we have received a + if we have then we can simply invalidate the address argument that we intend to pass to the go_trace routine*/ if ( (tok_is_next_token(PLUS_TOK) == SUCCESS)) valid_address = 0; else { /* otherwise we need to get the address that was given to us and to make sure that go_trace() knows that it is a valid address */ if ( (status = getarg_adr(&adr) ) != SUCCESS) return status; valid_address = 1; }; /* now lets call the routine that we need to do the work of this parser */ status = go_trace(adr,valid_address,go_or_trace); return status; #else return FOR_BOARD_ONLY; #endif }