opencsd: etmv4: ete: Add checks for bad program image

Check for inconsistencies in decode that could indicate
that the client has supplied an incorrect program image.

Checks for non-conditional branches associated with N atoms.

Check for inconsistencies in range addresses

Signed-off-by: Mike Leach <mike.leach@linaro.org>
diff --git a/decoder/include/opencsd/etmv4/trc_pkt_decode_etmv4i.h b/decoder/include/opencsd/etmv4/trc_pkt_decode_etmv4i.h
index 9b6f531..3b87497 100644
--- a/decoder/include/opencsd/etmv4/trc_pkt_decode_etmv4i.h
+++ b/decoder/include/opencsd/etmv4/trc_pkt_decode_etmv4i.h
@@ -111,8 +111,11 @@
     // sequencing error on packet processing - optionally continue
     ocsd_err_t handlePacketSeqErr(ocsd_err_t err, ocsd_trc_index_t index, const char *reason);
 
+    // inconsistent image for decode - optionally reset and continue
+    ocsd_err_t handleBadImageError(ocsd_trc_index_t index, const char* reason);
+
     // common packet error routine
-    ocsd_err_t handlePacketErr(ocsd_err_t err, ocsd_err_severity_t sev, ocsd_trc_index_t index, const char *reason);
+    ocsd_err_t handlePacketErr(ocsd_err_t err, ocsd_err_severity_t sev, ocsd_trc_index_t index, const char *reason, const unsync_info_t unsync_reason);
 
     ocsd_err_t addElemCC(TrcStackElemParam *pParamElem);
     ocsd_err_t addElemTS(TrcStackElemParam *pParamElem, bool withCC);
@@ -251,6 +254,34 @@
 
     TrcAddrReturnStack m_return_stack;  //!< the address return stack.
 
+    // range address check to look for possible incorrect input code memory images.
+    struct {
+        ocsd_vaddr_t next_st_addr;  // expected address for next range
+        bool valid;
+    } m_next_range_check;
+
+    void nextRangeCheckClear() {
+        m_next_range_check.valid = false;
+    };
+
+    void nextRangeCheckSet(const ocsd_vaddr_t addr) {
+        m_next_range_check.valid = true;
+        m_next_range_check.next_st_addr = addr;
+    };
+
+    bool nextRangeCheckOK(const ocsd_vaddr_t addr) {
+        if (m_next_range_check.valid) {
+            return (bool)(m_next_range_check.next_st_addr == addr);
+        }
+        // no check info - just return OK
+        return true;
+    };
+
+    // consistency check flags
+    bool m_direct_br_chk;
+    bool m_strict_br_chk;
+    bool m_range_cont_chk;
+
 //** output element handling
     OcsdGenElemStack m_out_elem;  //!< output element stack.
     OcsdTraceElement &outElem() { return m_out_elem.getCurrElem(); };   //!< current  out element
diff --git a/decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h b/decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h
index 2a03b08..b2c17a5 100644
--- a/decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h
+++ b/decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h
@@ -387,7 +387,10 @@
 
 #define ETE_ARCH_VERSION 0x5
 
-#define ETE_OPFLG_PKTDEC_SRCADDR_N_ATOMS 0x00010000 /**< Split source address output ranges for N-atoms */
+#define ETE_OPFLG_PKTDEC_SRCADDR_N_ATOMS    0x00010000 /**< Split source address output ranges for N-atoms */
+#define ETM4_OPFLG_PKTDEC_AA64_OPCODE_CHK   0x00020000 /**< check for invalid AA64 opcodes. (MSW == 0x0000) */
+
+#define ETE_ETM4_OPFLG_MASK (ETE_OPFLG_PKTDEC_SRCADDR_N_ATOMS | ETM4_OPFLG_PKTDEC_AA64_OPCODE_CHK)
 
 /** @}*/
 /** @}*/
diff --git a/decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp b/decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp
index c557998..3c786c6 100644
--- a/decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp
+++ b/decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp
@@ -39,8 +39,9 @@
 
 #define DCD_NAME "DCD_ETMV4"
 
-static const uint32_t ETMV4_SUPPORTED_DECODE_OP_FLAGS = OCSD_OPFLG_PKTDEC_COMMON |
-                        ETE_OPFLG_PKTDEC_SRCADDR_N_ATOMS;
+static const uint32_t ETMV4_SUPPORTED_DECODE_OP_FLAGS =
+    OCSD_OPFLG_PKTDEC_COMMON |  /* common op flags */
+    ETE_ETM4_OPFLG_MASK;        /* ete - etm4 op flags */
 
 TrcPktDecodeEtmV4I::TrcPktDecodeEtmV4I()
     : TrcPktDecodeBase(DCD_NAME)
@@ -118,13 +119,18 @@
             err = decodePacket();
             if (err)
             {
-#ifdef OCSD_WARN_UNSUPPORTED
-                if (err == OCSD_ERR_UNSUPP_DECODE_PKT)
-                    resp = OCSD_RESP_WARN_CONT;
+                // may want to continue through bad packets
+                if ((err == OCSD_ERR_BAD_DECODE_PKT) || (err == OCSD_ERR_UNSUPP_DECODE_PKT))
+                {
+                    if (getComponentOpMode() & OCSD_OPFLG_PKTDEC_HALT_BAD_PKTS)
+                        resp = OCSD_RESP_FATAL_INVALID_DATA;
+                    else if (getComponentOpMode() & OCSD_OPFLG_PKTDEC_ERROR_BAD_PKTS)
+                        resp = OCSD_RESP_ERR_CONT;
+                    else
+                        resp = OCSD_RESP_WARN_CONT;
+                }
                 else
-#else
-                resp = OCSD_RESP_FATAL_INVALID_DATA;
-#endif
+                    resp = OCSD_RESP_FATAL_INVALID_DATA;
 
                 bPktDone = true;
             }
@@ -227,6 +233,12 @@
         err = OCSD_ERR_HW_CFG_UNSUPP;
         LogError(ocsdError(OCSD_ERR_SEV_ERROR,OCSD_ERR_HW_CFG_UNSUPP,"ETMv4 instruction decode : Trace on conditional non-branch elements not supported."));
     }
+
+    // set consistency check flags 
+    m_direct_br_chk = (bool)(getComponentOpMode() & OCSD_OPFLG_N_UNCOND_DIR_BR_CHK);
+    m_strict_br_chk = (bool)(getComponentOpMode() & OCSD_OPFLG_STRICT_N_UNCOND_BR_CHK);
+    m_range_cont_chk = (bool)(getComponentOpMode() & OCSD_OPFLG_CHK_RANGE_CONTINUE);
+    
     return err;
 }
 
@@ -285,6 +297,7 @@
     m_last_IS = 0;
     clearElemRes();
     m_ete_first_ts_marker = false;
+    nextRangeCheckClear();
 
     // elements associated with data trace
 #ifdef DATA_TRACE_SUPPORTED
@@ -604,20 +617,7 @@
     case ETM4_PKT_I_NUM_DS_MKR:
     case ETM4_PKT_I_UNNUM_DS_MKR:
         // all currently unsupported
-        {
-        ocsd_err_severity_t sev = OCSD_ERR_SEV_ERROR;
-#ifdef OCSD_WARN_UNSUPPORTED
-        sev = OCSD_ERR_SEV_WARN;
-        //resp = OCSD_RESP_WARN_CONT;
-#else
-        //resp = OCSD_RESP_FATAL_INVALID_DATA;
-#endif
-        err = OCSD_ERR_UNSUPP_DECODE_PKT;
-        if (sev == OCSD_ERR_SEV_WARN)
-                        LogError(ocsdError(sev, err, "Data trace related, unsupported packet type."));
-        else 
-            err = handlePacketSeqErr(err, m_index_curr_pkt, "Data trace related, unsupported packet type.");
-        }
+        err = handlePacketSeqErr(OCSD_ERR_UNSUPP_DECODE_PKT, m_index_curr_pkt, "Data trace related, unsupported packet type.");
         break;
 
     default:
@@ -698,8 +698,13 @@
                     err = discardElements();
             }
 
-            if (err != OCSD_OK)
-                resp = OCSD_RESP_FATAL_INVALID_DATA;
+            if (err != OCSD_OK) {
+                // has the error reset the decoder?
+                if (m_curr_state == NO_SYNC)
+                    resp = OCSD_RESP_ERR_CONT;
+                else
+                    resp = OCSD_RESP_FATAL_INVALID_DATA;
+            }
         }
         
         // break out on error or wait request.
@@ -746,6 +751,7 @@
             {
                 // indicates a trace restart - beginning of trace or discontinuiuty
             case P0_TRC_ON:
+                nextRangeCheckClear();
                 err = m_out_elem.addElemType(pElem->getRootIndex(), OCSD_GEN_TRC_ELEM_TRACE_ON);
                 if (!err)
                 {
@@ -814,17 +820,20 @@
                     {
                         ocsd_atm_val atom = pAtomElem->commitOldest();
 
-                        // check if prev atom left us an indirect address target on the return stack
+                        // check if prev atom was indirect branch - may need address from return stack
                         if ((err = returnStackPop()) != OCSD_OK)
                             break;
 
                         // if address and context do instruction trace follower.
                         // otherwise skip atom and reduce committed elements
+                        // allow for insufficient program image.
                         if (!m_need_ctxt && !m_need_addr)
                         {
-                            err = processAtom(atom);
+                            if ((err = processAtom(atom)) != OCSD_OK)
+                                break;
                         }
-                        m_elem_res.P0_commit--; // mark committed 
+                        if (m_elem_res.P0_commit)
+                            m_elem_res.P0_commit--; // mark committed 
                     }
                     if (!pAtomElem->isEmpty())
                         bPopElem = false;   // don't remove if still atoms to process.
@@ -837,11 +846,13 @@
                 if ((err = returnStackPop()) != OCSD_OK)
                     break;
 
+                nextRangeCheckClear();
                 err = processException();  // output trace + exception elements.
                 m_elem_res.P0_commit--;
                 break;
 
             case P0_EXCEP_RET:
+                nextRangeCheckClear();
                 err = m_out_elem.addElemType(pElem->getRootIndex(), OCSD_GEN_TRC_ELEM_EXCEPTION_RET);
                 if (!err)
                 {
@@ -858,11 +869,13 @@
                 break;
 
             case P0_SRC_ADDR:
+                nextRangeCheckClear();
                 err = processSourceAddress();
                 m_elem_res.P0_commit--;
                 break;
 
             case P0_Q:
+                nextRangeCheckClear();
                 err = processQElement();
                 m_elem_res.P0_commit--;
 				break;
@@ -873,6 +886,7 @@
             case P0_TRANS_COMMIT:
             case P0_TRANS_FAIL:
             case P0_TRANS_TRACE_INIT:
+                nextRangeCheckClear();
                 err = processTransElem(pElem);
                 break;
 
@@ -1352,6 +1366,20 @@
                     m_return_stack.push(nextAddr, m_instr_info.isa);
 
             }
+            else if (m_direct_br_chk || m_strict_br_chk)  // consistency checks on N atoms?
+            {
+                // N atom - but direct branch instruction not conditional - bad input image?
+                if (!m_instr_info.is_conditional)
+                {
+                    // Some ETM IP incorrectly trace a taken branch to next instruction as N
+                    // look for branch where it is not next instruction if direct branch checks only
+                    if (((m_instr_info.branch_addr != nextAddr) && m_direct_br_chk) || m_strict_br_chk)
+                    {
+                        err = handleBadImageError(pElem->getRootIndex(), "Bad program image - N Atom on unconditional direct BR.\n");
+                        return err;
+                    }
+                }
+            }
             break;
 
         case OCSD_INSTR_BR_INDIRECT:
@@ -1360,17 +1388,45 @@
                 m_need_addr = true; // indirect branch taken - need new address.
                 if (m_instr_info.is_link)
                     m_return_stack.push(nextAddr,m_instr_info.isa);
-                m_return_stack.set_pop_pending();  // need to know next packet before we know what is to happen
+
+                // mark last atom as BR indirect - if no address next need addr from return stack.
+                m_return_stack.set_pop_pending();  
 
                 /* ETE does not have ERET trace packets - however to maintain the illusion if we see an ERET
                    output a gen elem ERET packet */
                 if (isETEConfig() && (m_instr_info.sub_type == OCSD_S_INSTR_V8_ERET))
                     ETE_ERET = true;
             }
+            else if (m_strict_br_chk) // consistency checks on N atoms?
+            {
+                // N atom - check if conditional - only in strict check mode.
+                if (!m_instr_info.is_conditional)
+                {
+                    err = handleBadImageError(pElem->getRootIndex(), "Bad program image - N Atom on unconditional indirect BR.\n");
+                    return err;
+                }
+            }
             break;
         }
         setElemTraceRange(outElem(), addr_range, (atom == ATOM_E), pElem->getRootIndex());
 
+        // check for discontinuity in address ranges where incorrect memory images supplied to decoder.
+        if (m_range_cont_chk)
+        {
+            // do the previous range chack.
+            if (!nextRangeCheckOK(addr_range.st_addr))
+            {
+                err = handleBadImageError(pElem->getRootIndex(), "Discontinuous ranges - Inconsistent program image for decode\n");
+                return err;
+            }
+
+            if (atom == ATOM_N)
+                // branch not taken - expect next range to be continuous
+                nextRangeCheckSet(nextAddr);
+            else
+                nextRangeCheckClear();  // branch taken - not continuous
+        }
+
         if (ETE_ERET)
         {
             err = m_out_elem.addElemType(pElem->getRootIndex(), OCSD_GEN_TRC_ELEM_EXCEPTION_RET);
@@ -1382,6 +1438,7 @@
     {
         // no waypoint - likely inaccessible memory range.
         m_need_addr = true; // need an address update 
+        nextRangeCheckClear();
 
         if(addr_range.st_addr != addr_range.en_addr)
         {
@@ -1970,15 +2027,20 @@
     if (getComponentOpMode() & OCSD_OPFLG_PKTDEC_ERROR_BAD_PKTS)
         sev = OCSD_ERR_SEV_ERROR;
 
-    return handlePacketErr(OCSD_ERR_BAD_DECODE_PKT, sev, index, reason);
+    return handlePacketErr(OCSD_ERR_BAD_DECODE_PKT, sev, index, reason, UNSYNC_BAD_PACKET);
 }
 
 ocsd_err_t TrcPktDecodeEtmV4I::handlePacketSeqErr(ocsd_err_t err, ocsd_trc_index_t index, const char *reason)
 {
-    return handlePacketErr(err, OCSD_ERR_SEV_ERROR, index, reason);
+    return handlePacketErr(err, OCSD_ERR_SEV_ERROR, index, reason, UNSYNC_BAD_PACKET);
 }
 
-ocsd_err_t TrcPktDecodeEtmV4I::handlePacketErr(ocsd_err_t err, ocsd_err_severity_t sev, ocsd_trc_index_t index, const char *reason)
+ocsd_err_t TrcPktDecodeEtmV4I::handleBadImageError(ocsd_trc_index_t index, const char* reason)
+{
+    return handlePacketErr(OCSD_ERR_BAD_DECODE_IMAGE, OCSD_ERR_SEV_ERROR, index, reason, UNSYNC_BAD_IMAGE);
+}
+
+ocsd_err_t TrcPktDecodeEtmV4I::handlePacketErr(ocsd_err_t err, ocsd_err_severity_t sev, ocsd_trc_index_t index, const char *reason, const unsync_info_t unsync_reason)
 {
     bool resetOnBadPackets = true;
 
@@ -1992,8 +2054,8 @@
         // switch to unsync - clear decode state
         resetDecoder();
         m_curr_state = NO_SYNC;
-        m_unsync_eot_info = UNSYNC_BAD_PACKET;
-        err = OCSD_OK;
+        m_unsync_eot_info = unsync_reason;
+        //err = OCSD_OK;
     }
     return err;
 
diff --git a/decoder/source/ocsd_dcd_tree.cpp b/decoder/source/ocsd_dcd_tree.cpp
index 9b1cae5..79aabbc 100644
--- a/decoder/source/ocsd_dcd_tree.cpp
+++ b/decoder/source/ocsd_dcd_tree.cpp
@@ -444,6 +444,10 @@
         crtFlags |= OCSD_CREATE_FLG_INST_ID;
     }
 
+    // check for the aa64 check.
+    if (createFlags & ETM4_OPFLG_PKTDEC_AA64_OPCODE_CHK)
+        s_instruction_decoder.setAA64_errOnBadOpcode(true);
+
     // create the decode element to attach to the channel.
     if((err = createDecodeElement(CSID)) != OCSD_OK)
         return err;