2 * \file trc_pkt_decode_ptm.cpp
3 * \brief OpenCSD : PTM packet decoder.
5 * \copyright Copyright (c) 2016, ARM Limited. All Rights Reserved.
9 * Redistribution and use in source and binary forms, with or without modification,
10 * are permitted provided that the following conditions are met:
12 * 1. Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
19 * 3. Neither the name of the copyright holder nor the names of its contributors
20 * may be used to endorse or promote products derived from this software without
21 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #include "opencsd/ptm/trc_pkt_decode_ptm.h"
38 #define DCD_NAME "DCD_PTM"
40 TrcPktDecodePtm::TrcPktDecodePtm()
41 : TrcPktDecodeBase(DCD_NAME)
46 TrcPktDecodePtm::TrcPktDecodePtm(int instIDNum)
47 : TrcPktDecodeBase(DCD_NAME,instIDNum)
52 TrcPktDecodePtm::~TrcPktDecodePtm()
56 /*********************** implementation packet decoding interface */
58 ocsd_datapath_resp_t TrcPktDecodePtm::processPacket()
60 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
61 bool bPktDone = false;
68 // no sync - output a no sync packet then transition to wait sync.
69 m_output_elem.elem_type = OCSD_GEN_TRC_ELEM_NO_SYNC;
70 resp = outputTraceElement(m_output_elem);
71 m_curr_state = (m_curr_packet_in->getType() == PTM_PKT_A_SYNC) ? WAIT_ISYNC : WAIT_SYNC;
76 if(m_curr_packet_in->getType() == PTM_PKT_A_SYNC)
77 m_curr_state = WAIT_ISYNC;
82 if(m_curr_packet_in->getType() == PTM_PKT_I_SYNC)
83 m_curr_state = DECODE_PKTS;
89 resp = decodePacket();
94 // should only see these after a _WAIT resp - in flush handler
98 // throw a decoder error
105 ocsd_datapath_resp_t TrcPktDecodePtm::onEOT()
107 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
108 // shouldn't be any packets left to be processed - flush shoudl have done this.
109 // just output the end of trace marker
110 m_output_elem.setType(OCSD_GEN_TRC_ELEM_EO_TRACE);
111 resp = outputTraceElement(m_output_elem);
115 ocsd_datapath_resp_t TrcPktDecodePtm::onReset()
117 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
122 ocsd_datapath_resp_t TrcPktDecodePtm::onFlush()
124 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
125 resp = contProcess();
129 // atom and isync packets can have multiple ouput packets that can be _WAITed mid stream.
130 ocsd_datapath_resp_t TrcPktDecodePtm::contProcess()
132 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
136 resp = processIsync();
140 resp = processAtom();
144 resp = processWPUpdate();
148 resp = processBranch();
151 default: break; // not a state that requires further processing
154 if(OCSD_DATA_RESP_IS_CONT(resp) && processStateIsCont())
155 m_curr_state = DECODE_PKTS; // continue packet processing - assuming we have not degraded into an unsynced state.
160 ocsd_err_t TrcPktDecodePtm::onProtocolConfig()
162 ocsd_err_t err = OCSD_OK;
164 return OCSD_ERR_NOT_INIT;
166 // static config - copy of CSID for easy reference
167 m_CSID = m_config->getTraceID();
169 // handle return stack implementation
170 if (m_config->hasRetStack())
172 m_return_stack.set_active(m_config->enaRetStack());
173 #ifdef TRC_RET_STACK_DEBUG
174 m_return_stack.set_dbg_logger(this);
178 // config options affecting decode
179 m_instr_info.pe_type.profile = m_config->coreProfile();
180 m_instr_info.pe_type.arch = m_config->archVersion();
181 m_instr_info.dsb_dmb_waypoints = m_config->dmsbWayPt() ? 1 : 0;
182 m_instr_info.wfi_wfe_branch = 0;
186 /****************** local decoder routines */
188 void TrcPktDecodePtm::initDecoder()
191 m_instr_info.pe_type.profile = profile_Unknown;
192 m_instr_info.pe_type.arch = ARCH_UNKNOWN;
193 m_instr_info.dsb_dmb_waypoints = 0;
197 void TrcPktDecodePtm::resetDecoder()
199 m_curr_state = NO_SYNC;
200 m_need_isync = true; // need context to start.
202 m_instr_info.isa = ocsd_isa_unknown;
203 m_mem_nacc_pending = false;
205 m_pe_context.ctxt_id_valid = 0;
206 m_pe_context.bits64 = 0;
207 m_pe_context.vmid_valid = 0;
208 m_pe_context.exception_level = ocsd_EL_unknown;
209 m_pe_context.security_level = ocsd_sec_secure;
210 m_pe_context.el_valid = 0;
212 m_curr_pe_state.instr_addr = 0x0;
213 m_curr_pe_state.isa = ocsd_isa_unknown;
214 m_curr_pe_state.valid = false;
217 m_output_elem.init();
220 ocsd_datapath_resp_t TrcPktDecodePtm::decodePacket()
222 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
223 switch(m_curr_packet_in->getType())
225 // ignore these from trace o/p point of veiw
226 case PTM_PKT_NOTSYNC:
227 case PTM_PKT_INCOMPLETE_EOT:
228 case PTM_PKT_NOERROR:
231 // bad / reserved packet - need to wait for next sync point
232 case PTM_PKT_BAD_SEQUENCE:
233 case PTM_PKT_RESERVED:
234 m_curr_state = WAIT_SYNC;
235 m_need_isync = true; // need context to re-start.
236 m_output_elem.setType(OCSD_GEN_TRC_ELEM_NO_SYNC);
237 resp = outputTraceElement(m_output_elem);
240 // packets we can ignore if in sync
247 resp = processIsync();
250 case PTM_PKT_BRANCH_ADDRESS:
251 resp = processBranch();
254 case PTM_PKT_TRIGGER:
255 m_output_elem.setType(OCSD_GEN_TRC_ELEM_EVENT);
256 m_output_elem.setEvent(EVENT_TRIGGER, 0);
257 resp = outputTraceElement(m_output_elem);
260 case PTM_PKT_WPOINT_UPDATE:
261 resp = processWPUpdate();
264 case PTM_PKT_CONTEXT_ID:
267 // see if this is a change
268 if((m_pe_context.ctxt_id_valid) && (m_pe_context.context_id == m_curr_packet_in->context.ctxtID))
272 m_pe_context.context_id = m_curr_packet_in->context.ctxtID;
273 m_pe_context.ctxt_id_valid = 1;
274 m_output_elem.setType(OCSD_GEN_TRC_ELEM_PE_CONTEXT);
275 m_output_elem.setContext(m_pe_context);
276 resp = outputTraceElement(m_output_elem);
284 // see if this is a change
285 if((m_pe_context.vmid_valid) && (m_pe_context.vmid == m_curr_packet_in->context.VMID))
289 m_pe_context.vmid = m_curr_packet_in->context.VMID;
290 m_pe_context.vmid_valid = 1;
291 m_output_elem.setType(OCSD_GEN_TRC_ELEM_PE_CONTEXT);
292 m_output_elem.setContext(m_pe_context);
293 resp = outputTraceElement(m_output_elem);
299 if(m_curr_pe_state.valid)
301 m_atoms.initAtomPkt(m_curr_packet_in->getAtom(),m_index_curr_pkt);
302 resp = processAtom();
306 case PTM_PKT_TIMESTAMP:
307 m_output_elem.setType(OCSD_GEN_TRC_ELEM_TIMESTAMP);
308 m_output_elem.timestamp = m_curr_packet_in->timestamp;
309 if(m_curr_packet_in->cc_valid)
310 m_output_elem.setCycleCount(m_curr_packet_in->cycle_count);
311 resp = outputTraceElement(m_output_elem);
314 case PTM_PKT_EXCEPTION_RET:
315 m_output_elem.setType(OCSD_GEN_TRC_ELEM_EXCEPTION_RET);
316 resp = outputTraceElement(m_output_elem);
323 ocsd_datapath_resp_t TrcPktDecodePtm::processIsync()
325 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
327 // extract the I-Sync data if not re-entering after a _WAIT
328 if(m_curr_state == DECODE_PKTS)
330 m_curr_pe_state.instr_addr = m_curr_packet_in->getAddrVal();
331 m_curr_pe_state.isa = m_curr_packet_in->getISA();
332 m_curr_pe_state.valid = true;
334 m_i_sync_pe_ctxt = m_curr_packet_in->ISAChanged();
335 if(m_curr_packet_in->CtxtIDUpdated())
337 m_pe_context.context_id = m_curr_packet_in->getCtxtID();
338 m_pe_context.ctxt_id_valid = 1;
339 m_i_sync_pe_ctxt = true;
342 if(m_curr_packet_in->VMIDUpdated())
344 m_pe_context.vmid = m_curr_packet_in->getVMID();
345 m_pe_context.vmid_valid = 1;
346 m_i_sync_pe_ctxt = true;
348 m_pe_context.security_level = m_curr_packet_in->getNS() ? ocsd_sec_nonsecure : ocsd_sec_secure;
350 if(m_need_isync || (m_curr_packet_in->iSyncReason() != iSync_Periodic))
352 m_output_elem.setType(OCSD_GEN_TRC_ELEM_TRACE_ON);
353 m_output_elem.trace_on_reason = TRACE_ON_NORMAL;
354 if(m_curr_packet_in->iSyncReason() == iSync_TraceRestartAfterOverflow)
355 m_output_elem.trace_on_reason = TRACE_ON_OVERFLOW;
356 else if(m_curr_packet_in->iSyncReason() == iSync_DebugExit)
357 m_output_elem.trace_on_reason = TRACE_ON_EX_DEBUG;
358 if(m_curr_packet_in->hasCC())
359 m_output_elem.setCycleCount(m_curr_packet_in->getCCVal());
360 resp = outputTraceElement(m_output_elem);
364 // periodic - no output
365 m_i_sync_pe_ctxt = false;
367 m_need_isync = false; // got 1st Isync - can continue to process data.
368 m_return_stack.flush();
371 if(m_i_sync_pe_ctxt && OCSD_DATA_RESP_IS_CONT(resp))
373 m_output_elem.setType(OCSD_GEN_TRC_ELEM_PE_CONTEXT);
374 m_output_elem.setContext(m_pe_context);
375 m_output_elem.setISA(m_curr_pe_state.isa);
376 resp = outputTraceElement(m_output_elem);
377 m_i_sync_pe_ctxt = false;
380 // if wait and still stuff to process....
381 if(OCSD_DATA_RESP_IS_WAIT(resp) && ( m_i_sync_pe_ctxt))
382 m_curr_state = CONT_ISYNC;
387 // change of address and/or exception in program flow.
388 // implies E atom before the branch if none exception.
389 ocsd_datapath_resp_t TrcPktDecodePtm::processBranch()
391 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
393 // initial pass - decoding packet.
394 if(m_curr_state == DECODE_PKTS)
396 // specific behviour if this is an exception packet.
397 if(m_curr_packet_in->isBranchExcepPacket())
399 // exception - record address and output exception packet.
400 m_output_elem.setType(OCSD_GEN_TRC_ELEM_EXCEPTION);
401 m_output_elem.exception_number = m_curr_packet_in->excepNum();
402 m_output_elem.excep_ret_addr = 0;
403 if(m_curr_pe_state.valid)
405 m_output_elem.excep_ret_addr = 1;
406 m_output_elem.en_addr = m_curr_pe_state.instr_addr;
408 // could be an associated cycle count
409 if(m_curr_packet_in->hasCC())
410 m_output_elem.setCycleCount(m_curr_packet_in->getCCVal());
412 // output the element
413 resp = outputTraceElement(m_output_elem);
417 // branch address only - implies E atom - need to output a range element based on the atom.
418 if(m_curr_pe_state.valid)
419 resp = processAtomRange(ATOM_E,"BranchAddr");
422 // now set the branch address for the next time.
423 m_curr_pe_state.isa = m_curr_packet_in->getISA();
424 m_curr_pe_state.instr_addr = m_curr_packet_in->getAddrVal();
425 m_curr_pe_state.valid = true;
428 // atom range may return with NACC pending
429 checkPendingNacc(resp);
431 // if wait and still stuff to process....
432 if(OCSD_DATA_RESP_IS_WAIT(resp) && ( m_mem_nacc_pending))
433 m_curr_state = CONT_BRANCH;
438 // effectively completes a range prior to exception or after many bytes of trace (>4096)
440 ocsd_datapath_resp_t TrcPktDecodePtm::processWPUpdate()
442 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
444 // if we need an address to run from then the WPUpdate will not form a range as
445 // we do not have a start point - still waiting for branch or other address packet
446 if(m_curr_pe_state.valid)
448 // WP update implies atom - use E, we cannot be sure if the instruction passed its condition codes
449 // - though it doesn't really matter as it is not a branch so cannot change flow.
450 resp = processAtomRange(ATOM_E,"WP update",TRACE_TO_ADDR_INCL,m_curr_packet_in->getAddrVal());
453 // atom range may return with NACC pending
454 checkPendingNacc(resp);
456 // if wait and still stuff to process....
457 if(OCSD_DATA_RESP_IS_WAIT(resp) && ( m_mem_nacc_pending))
458 m_curr_state = CONT_WPUP;
463 // a single atom packet can result in multiple range outputs...need to be re-entrant in case we get a wait response.
464 // also need to handle nacc response from instruction walking routine
466 ocsd_datapath_resp_t TrcPktDecodePtm::processAtom()
468 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
470 // loop to process all the atoms in the packet
471 while(m_atoms.numAtoms() && m_curr_pe_state.valid && OCSD_DATA_RESP_IS_CONT(resp))
473 resp = processAtomRange(m_atoms.getCurrAtomVal(),"atom");
474 if(!m_curr_pe_state.valid)
480 // bad address may mean a nacc needs sending
481 checkPendingNacc(resp);
483 // if wait and still stuff to process....
484 if(OCSD_DATA_RESP_IS_WAIT(resp) && ( m_mem_nacc_pending || m_atoms.numAtoms()))
485 m_curr_state = CONT_ATOM;
490 void TrcPktDecodePtm::checkPendingNacc(ocsd_datapath_resp_t &resp)
492 if(m_mem_nacc_pending && OCSD_DATA_RESP_IS_CONT(resp))
494 m_output_elem.setType(OCSD_GEN_TRC_ELEM_ADDR_NACC);
495 m_output_elem.st_addr = m_nacc_addr;
496 resp = outputTraceElementIdx(m_index_curr_pkt,m_output_elem);
497 m_mem_nacc_pending = false;
501 // given an atom element - walk the code and output a range or mark nacc.
502 ocsd_datapath_resp_t TrcPktDecodePtm::processAtomRange(const ocsd_atm_val A, const char *pkt_msg, const waypoint_trace_t traceWPOp /*= TRACE_WAYPOINT*/, const ocsd_vaddr_t nextAddrMatch /*= 0*/)
504 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
505 bool bWPFound = false;
506 std::ostringstream oss;
508 m_instr_info.instr_addr = m_curr_pe_state.instr_addr;
509 m_instr_info.isa = m_curr_pe_state.isa;
511 ocsd_err_t err = traceInstrToWP(bWPFound,traceWPOp,nextAddrMatch);
514 if(err == OCSD_ERR_UNSUPPORTED_ISA)
516 m_curr_pe_state.valid = false; // need a new address packet
517 oss << "Warning: unsupported instruction set processing " << pkt_msg << " packet.";
518 LogError(ocsdError(OCSD_ERR_SEV_WARN,err,m_index_curr_pkt,m_CSID,oss.str()));
519 // wait for next address
520 return OCSD_RESP_WARN_CONT;
524 resp = OCSD_RESP_FATAL_INVALID_DATA;
525 oss << "Error processing " << pkt_msg << " packet.";
526 LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,m_index_curr_pkt,m_CSID,oss.str()));
533 // save recorded next instuction address
534 ocsd_vaddr_t nextAddr = m_instr_info.instr_addr;
536 // action according to waypoint type and atom value
537 switch(m_instr_info.type)
542 m_instr_info.instr_addr = m_instr_info.branch_addr;
543 if (m_instr_info.is_link)
544 m_return_stack.push(nextAddr,m_instr_info.isa);
548 // For PTM -> branch addresses imply E atom, N atom does not need address (return stack will require this)
549 case OCSD_INSTR_BR_INDIRECT:
552 // atom on indirect branch - either implied E from a branch address packet, or return stack if active.
554 // indirect branch taken - need new address -if the current packet is a branch address packet this will be sorted.
555 m_curr_pe_state.valid = false;
557 // if return stack and the incoming packet is an atom.
558 if (m_return_stack.is_active() && (m_curr_packet_in->getType() == PTM_PKT_ATOM))
560 // we have an E atom packet and return stack value - set address from return stack
561 m_instr_info.instr_addr = m_return_stack.pop(m_instr_info.next_isa);
563 if (m_return_stack.overflow())
565 resp = OCSD_RESP_FATAL_INVALID_DATA;
566 oss << "Return stack error processing " << pkt_msg << " packet.";
567 LogError(ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_RET_STACK_OVERFLOW, m_index_curr_pkt, m_CSID, oss.str()));
571 m_curr_pe_state.valid = true;
573 if(m_instr_info.is_link)
574 m_return_stack.push(nextAddr, m_instr_info.isa);
579 m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE);
580 m_output_elem.setLastInstrInfo((A == ATOM_E),m_instr_info.type, m_instr_info.sub_type,m_instr_info.instr_size);
581 m_output_elem.setISA(m_curr_pe_state.isa);
582 if(m_curr_packet_in->hasCC())
583 m_output_elem.setCycleCount(m_curr_packet_in->getCCVal());
584 m_output_elem.setLastInstrCond(m_instr_info.is_conditional);
585 resp = outputTraceElementIdx(m_index_curr_pkt,m_output_elem);
587 m_curr_pe_state.instr_addr = m_instr_info.instr_addr;
588 m_curr_pe_state.isa = m_instr_info.next_isa;
592 // no waypoint - likely inaccessible memory range.
593 m_curr_pe_state.valid = false; // need an address update
595 if(m_output_elem.st_addr != m_output_elem.en_addr)
597 // some trace before we were out of memory access range
598 m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE);
599 m_output_elem.setLastInstrInfo(true,m_instr_info.type, m_instr_info.sub_type,m_instr_info.instr_size);
600 m_output_elem.setISA(m_curr_pe_state.isa);
601 m_output_elem.setLastInstrCond(m_instr_info.is_conditional);
602 resp = outputTraceElementIdx(m_index_curr_pkt,m_output_elem);
608 ocsd_err_t TrcPktDecodePtm::traceInstrToWP(bool &bWPFound, const waypoint_trace_t traceWPOp /*= TRACE_WAYPOINT*/, const ocsd_vaddr_t nextAddrMatch /*= 0*/)
612 ocsd_err_t err = OCSD_OK;
613 ocsd_vaddr_t curr_op_address;
615 ocsd_mem_space_acc_t mem_space = (m_pe_context.security_level == ocsd_sec_secure) ? OCSD_MEM_SPACE_S : OCSD_MEM_SPACE_N;
617 m_output_elem.st_addr = m_output_elem.en_addr = m_instr_info.instr_addr;
618 m_output_elem.num_instr_range = 0;
622 while(!bWPFound && !m_mem_nacc_pending)
624 // start off by reading next opcode;
626 curr_op_address = m_instr_info.instr_addr; // save the start address for the current opcode
627 err = accessMemory(m_instr_info.instr_addr,mem_space,&bytesReq,(uint8_t *)&opcode);
628 if(err != OCSD_OK) break;
630 if(bytesReq == 4) // got data back
632 m_instr_info.opcode = opcode;
633 err = instrDecode(&m_instr_info);
634 if(err != OCSD_OK) break;
636 // increment address - may be adjusted by direct branch value later
637 m_instr_info.instr_addr += m_instr_info.instr_size;
639 // update the range decoded address in the output packet.
640 m_output_elem.en_addr = m_instr_info.instr_addr;
641 m_output_elem.num_instr_range++;
643 m_output_elem.last_i_type = m_instr_info.type;
644 // either walking to match the next instruction address or a real waypoint
645 if(traceWPOp != TRACE_WAYPOINT)
647 if(traceWPOp == TRACE_TO_ADDR_EXCL)
648 bWPFound = (m_output_elem.en_addr == nextAddrMatch);
650 bWPFound = (curr_op_address == nextAddrMatch);
653 bWPFound = (m_instr_info.type != OCSD_INSTR_OTHER);
657 // not enough memory accessible.
658 m_mem_nacc_pending = true;
659 m_nacc_addr = m_instr_info.instr_addr;
665 /* End of File trc_pkt_decode_ptm.cpp */