| /* |
| * \file trc_pkt_proc_ptm.cpp |
| * \brief OpenCSD : |
| * |
| * \copyright Copyright (c) 2015, ARM Limited. All Rights Reserved. |
| */ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * 3. Neither the name of the copyright holder nor the names of its contributors |
| * may be used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "opencsd/ptm/trc_pkt_proc_ptm.h" |
| #include "opencsd/ptm/trc_cmp_cfg_ptm.h" |
| #include "common/ocsd_error.h" |
| |
| |
| #ifdef __GNUC__ |
| // G++ doesn't like the ## pasting |
| #define PTM_PKTS_NAME "PKTP_PTM" |
| #else |
| // VC++ is OK |
| #define PTM_PKTS_NAME OCSD_CMPNAME_PREFIX_PKTPROC##"_PTM" |
| #endif |
| |
| TrcPktProcPtm::TrcPktProcPtm() : TrcPktProcBase(PTM_PKTS_NAME) |
| { |
| InitProcessorState(); |
| BuildIPacketTable(); |
| } |
| |
| TrcPktProcPtm::TrcPktProcPtm(int instIDNum) : TrcPktProcBase(PTM_PKTS_NAME, instIDNum) |
| { |
| InitProcessorState(); |
| BuildIPacketTable(); |
| } |
| |
| TrcPktProcPtm::~TrcPktProcPtm() |
| { |
| |
| } |
| |
| ocsd_err_t TrcPktProcPtm::onProtocolConfig() |
| { |
| ocsd_err_t err = OCSD_ERR_NOT_INIT; |
| |
| if(m_config != 0) |
| { |
| m_chanIDCopy = m_config->getTraceID(); |
| err = OCSD_OK; |
| } |
| return err; |
| } |
| |
| ocsd_datapath_resp_t TrcPktProcPtm::processData( const ocsd_trc_index_t index, |
| const uint32_t dataBlockSize, |
| const uint8_t *pDataBlock, |
| uint32_t *numBytesProcessed) |
| { |
| ocsd_datapath_resp_t resp = OCSD_RESP_CONT; |
| uint8_t currByte = 0; |
| |
| m_dataInProcessed = 0; |
| |
| if(!checkInit()) |
| { |
| resp = OCSD_RESP_FATAL_NOT_INIT; |
| } |
| else |
| { |
| m_pDataIn = pDataBlock; |
| m_dataInLen = dataBlockSize; |
| m_block_idx = index; // index start for current block |
| } |
| |
| while( ( ( m_dataInProcessed < dataBlockSize) || |
| (( m_dataInProcessed == dataBlockSize) && (m_process_state == SEND_PKT)) ) && |
| OCSD_DATA_RESP_IS_CONT(resp)) |
| { |
| try |
| { |
| switch(m_process_state) |
| { |
| case WAIT_SYNC: |
| if(!m_waitASyncSOPkt) |
| { |
| m_curr_pkt_index = m_block_idx + m_dataInProcessed; |
| m_curr_packet.type = PTM_PKT_NOTSYNC; |
| m_bAsyncRawOp = hasRawMon(); |
| } |
| resp = waitASync(); |
| break; |
| |
| case PROC_HDR: |
| m_curr_pkt_index = m_block_idx + m_dataInProcessed; |
| if(readByte(currByte)) |
| { |
| m_pIPktFn = m_i_table[currByte].pptkFn; |
| m_curr_packet.type = m_i_table[currByte].pkt_type; |
| } |
| else |
| { |
| // sequencing error - should not get to the point where readByte |
| // fails and m_DataInProcessed < dataBlockSize |
| // throw data overflow error |
| throw ocsdError(OCSD_ERR_SEV_ERROR,OCSD_ERR_PKT_INTERP_FAIL,m_curr_pkt_index,this->m_chanIDCopy,"Data Buffer Overrun"); |
| } |
| m_process_state = PROC_DATA; |
| |
| case PROC_DATA: |
| (this->*m_pIPktFn)(); |
| break; |
| |
| case SEND_PKT: |
| resp = outputPacket(); |
| InitPacketState(); |
| m_process_state = PROC_HDR; |
| break; |
| } |
| } |
| catch(ocsdError &err) |
| { |
| LogError(err); |
| if( (err.getErrorCode() == OCSD_ERR_BAD_PACKET_SEQ) || |
| (err.getErrorCode() == OCSD_ERR_INVALID_PCKT_HDR)) |
| { |
| // send invalid packets up the pipe to let the next stage decide what to do. |
| m_process_state = SEND_PKT; |
| } |
| else |
| { |
| // bail out on any other error. |
| resp = OCSD_RESP_FATAL_INVALID_DATA; |
| } |
| } |
| catch(...) |
| { |
| /// vv bad at this point. |
| resp = OCSD_RESP_FATAL_SYS_ERR; |
| const ocsdError &fatal = ocsdError(OCSD_ERR_SEV_ERROR,OCSD_ERR_FAIL,m_curr_pkt_index,m_chanIDCopy,"Unknown System Error decoding trace."); |
| LogError(fatal); |
| } |
| |
| } |
| *numBytesProcessed = m_dataInProcessed; |
| return resp; |
| } |
| |
| ocsd_datapath_resp_t TrcPktProcPtm::onEOT() |
| { |
| ocsd_datapath_resp_t err = OCSD_RESP_FATAL_NOT_INIT; |
| if(checkInit()) |
| { |
| err = OCSD_RESP_CONT; |
| if(m_currPacketData.size() > 0) |
| { |
| m_curr_packet.SetErrType(PTM_PKT_INCOMPLETE_EOT); |
| err = outputPacket(); |
| } |
| } |
| return err; |
| } |
| |
| ocsd_datapath_resp_t TrcPktProcPtm::onReset() |
| { |
| ocsd_datapath_resp_t err = OCSD_RESP_FATAL_NOT_INIT; |
| if(checkInit()) |
| { |
| InitProcessorState(); |
| err = OCSD_RESP_CONT; |
| } |
| return err; |
| } |
| |
| ocsd_datapath_resp_t TrcPktProcPtm::onFlush() |
| { |
| ocsd_datapath_resp_t err = OCSD_RESP_FATAL_NOT_INIT; |
| if(checkInit()) |
| { |
| err = OCSD_RESP_CONT; |
| } |
| return err; |
| } |
| |
| const bool TrcPktProcPtm::isBadPacket() const |
| { |
| return m_curr_packet.isBadPacket(); |
| } |
| |
| void TrcPktProcPtm::InitPacketState() |
| { |
| m_curr_packet.Clear(); |
| |
| } |
| |
| void TrcPktProcPtm::InitProcessorState() |
| { |
| m_curr_packet.SetType(PTM_PKT_NOTSYNC); |
| m_pIPktFn = &TrcPktProcPtm::pktReserved; |
| m_process_state = WAIT_SYNC; |
| m_async_0 = 0; |
| m_waitASyncSOPkt = false; |
| m_bAsyncRawOp = false; |
| m_bOPNotSyncPkt = false; |
| m_excepAltISA = 0; |
| |
| m_curr_packet.ResetState(); |
| InitPacketState(); |
| } |
| |
| const bool TrcPktProcPtm::readByte(uint8_t &currByte) |
| { |
| bool bValidByte = false; |
| |
| if(m_dataInProcessed < m_dataInLen) |
| { |
| currByte = m_pDataIn[m_dataInProcessed++]; |
| m_currPacketData.push_back(currByte); |
| bValidByte = true; |
| } |
| return bValidByte; |
| } |
| |
| void TrcPktProcPtm::unReadByte() |
| { |
| m_dataInProcessed--; |
| m_currPacketData.pop_back(); |
| } |
| |
| ocsd_datapath_resp_t TrcPktProcPtm::outputPacket() |
| { |
| ocsd_datapath_resp_t resp = OCSD_RESP_CONT; |
| resp = outputOnAllInterfaces(m_curr_pkt_index,&m_curr_packet,&m_curr_packet.type,m_currPacketData); |
| m_currPacketData.clear(); |
| return resp; |
| } |
| |
| /*** sync and packet functions ***/ |
| ocsd_datapath_resp_t TrcPktProcPtm::waitASync() |
| { |
| ocsd_datapath_resp_t resp = OCSD_RESP_CONT; |
| |
| // looking for possible patterns in input buffer:- |
| // a) ASYNC @ start : 00 00 00 00 00 80 |
| // b) unsync then async: xx xx xx xx xx xx xx xx 00 00 00 00 00 80 |
| // c) unsync (may have 00) xx xx xx xx 00 xx xx 00 00 00 xx xx xx xx |
| // d) unsync then part async: xx xx xx xx xx xx xx xx xx xx xx 00 00 00 |
| // e) unsync with prev part async [00 00 00] 00 xx xx xx xx xx xx xx xx [] = byte in previous input buffer |
| |
| // bytes to read before throwing an unsynced packet |
| #define UNSYNC_PKT_MAX 16 |
| static const uint8_t spare_zeros[] = { 0,0,0,0,0,0,0,0, |
| 0,0,0,0,0,0,0,0 }; |
| |
| bool doScan = true; |
| bool bSendUnsyncedData = false; |
| bool bHaveASync = false; |
| int unsynced_bytes = 0; |
| int unsync_scan_block_start = 0; |
| int pktBytesOnEntry = m_currPacketData.size(); // did we have part of a potential async last time? |
| |
| while(doScan && OCSD_DATA_RESP_IS_CONT(resp)) |
| { |
| // may have spotted the start of an async |
| if(m_waitASyncSOPkt == true) |
| { |
| switch(findAsync()) |
| { |
| case ASYNC: |
| case ASYNC_EXTRA_0: |
| m_process_state = SEND_PKT; |
| m_waitASyncSOPkt = false; |
| bSendUnsyncedData = true; |
| bHaveASync = true; |
| doScan = false; |
| break; |
| |
| case THROW_0: |
| // remove a bunch of 0s |
| unsynced_bytes += ASYNC_PAD_0_LIMIT; |
| m_waitASyncSOPkt = false; |
| m_currPacketData.erase( m_currPacketData.begin(), m_currPacketData.begin()+ASYNC_PAD_0_LIMIT); |
| break; |
| |
| case NOT_ASYNC: |
| unsynced_bytes += m_currPacketData.size(); |
| m_waitASyncSOPkt = false; |
| m_currPacketData.clear(); |
| break; |
| |
| case ASYNC_INCOMPLETE: |
| bSendUnsyncedData = true; |
| doScan = false; |
| break; |
| } |
| } |
| else |
| { |
| if(m_pDataIn[m_dataInProcessed++] == 0x00) |
| { |
| m_waitASyncSOPkt = true; |
| m_currPacketData.push_back(0); |
| m_async_0 = 1; |
| } |
| else |
| { |
| unsynced_bytes++; |
| } |
| } |
| |
| // may need to send some unsynced data here, either if we have enought to make it worthwhile, |
| // or are at the end of the buffer. |
| if(unsynced_bytes >= UNSYNC_PKT_MAX) |
| bSendUnsyncedData = true; |
| |
| if(m_dataInProcessed == m_dataInLen) |
| { |
| bSendUnsyncedData = true; |
| doScan = false; // no more data available - stop the scan |
| } |
| |
| // will send any unsynced data |
| if(bSendUnsyncedData && (unsynced_bytes > 0)) |
| { |
| if(m_bAsyncRawOp) |
| { |
| // there were some 0's in the packet buyffer from the last pass that are no longer in the raw buffer, |
| // and these turned out not to be an async |
| if(pktBytesOnEntry) |
| { |
| outputRawPacketToMonitor(m_curr_pkt_index,&m_curr_packet,pktBytesOnEntry,spare_zeros); |
| m_curr_pkt_index += pktBytesOnEntry; |
| } |
| outputRawPacketToMonitor(m_curr_pkt_index,&m_curr_packet,unsynced_bytes,m_pDataIn+unsync_scan_block_start); |
| } |
| if (!m_bOPNotSyncPkt) |
| { |
| resp = outputDecodedPacket(m_curr_pkt_index, &m_curr_packet); |
| m_bOPNotSyncPkt = true; |
| } |
| unsync_scan_block_start += unsynced_bytes; |
| m_curr_pkt_index+= unsynced_bytes; |
| unsynced_bytes = 0; |
| bSendUnsyncedData = false; |
| } |
| |
| // mark next packet as the ASYNC we are looking for. |
| if(bHaveASync) |
| m_curr_packet.SetType(PTM_PKT_A_SYNC); |
| } |
| |
| return resp; |
| } |
| |
| void TrcPktProcPtm::pktASync() |
| { |
| if(m_currPacketData.size() == 1) // header byte |
| { |
| m_async_0 = 1; |
| } |
| |
| switch(findAsync()) |
| { |
| case ASYNC: |
| case ASYNC_EXTRA_0: |
| m_process_state = SEND_PKT; |
| break; |
| |
| case THROW_0: |
| case NOT_ASYNC: |
| throwMalformedPacketErr("Bad Async packet"); |
| break; |
| |
| case ASYNC_INCOMPLETE: |
| break; |
| |
| } |
| } |
| |
| TrcPktProcPtm::async_result_t TrcPktProcPtm::findAsync() |
| { |
| async_result_t async_res = NOT_ASYNC; |
| bool bFound = false; // found non-zero byte in sequence |
| bool bByteAvail = true; |
| uint8_t currByte; |
| |
| while(!bFound && bByteAvail) |
| { |
| if(readByte(currByte)) |
| { |
| if(currByte == 0x00) |
| { |
| m_async_0++; |
| if(m_async_0 >= (ASYNC_PAD_0_LIMIT + ASYNC_REQ_0)) |
| { |
| bFound = true; |
| async_res = THROW_0; |
| } |
| } |
| else |
| { |
| if(currByte == 0x80) |
| { |
| if(m_async_0 == 5) |
| async_res = ASYNC; |
| else if(m_async_0 > 5) |
| async_res = ASYNC_EXTRA_0; |
| } |
| bFound = true; |
| } |
| } |
| else |
| { |
| bByteAvail = false; |
| async_res = ASYNC_INCOMPLETE; |
| } |
| } |
| return async_res; |
| } |
| |
| void TrcPktProcPtm::pktISync() |
| { |
| uint8_t currByte = 0; |
| int pktIndex = m_currPacketData.size() - 1; |
| bool bGotBytes = false, validByte = true; |
| |
| if(pktIndex == 0) |
| { |
| m_numCtxtIDBytes = m_config->CtxtIDBytes(); |
| m_gotCtxtIDBytes = 0; |
| |
| // total bytes = 6 + ctxtID; (perhaps more later) |
| m_numPktBytesReq = 6 + m_numCtxtIDBytes; |
| } |
| |
| while(validByte && !bGotBytes) |
| { |
| if(readByte(currByte)) |
| { |
| pktIndex = m_currPacketData.size() - 1; |
| if(pktIndex == 5) |
| { |
| // got the info byte |
| int altISA = (currByte >> 2) & 0x1; |
| int reason = (currByte >> 5) & 0x3; |
| m_curr_packet.SetISyncReason((ocsd_iSync_reason)(reason)); |
| m_curr_packet.UpdateNS((currByte >> 3) & 0x1); |
| m_curr_packet.UpdateAltISA((currByte >> 2) & 0x1); |
| m_curr_packet.UpdateHyp((currByte >> 1) & 0x1); |
| |
| ocsd_isa isa = ocsd_isa_arm; |
| if(m_currPacketData[1] & 0x1) |
| isa = altISA ? ocsd_isa_tee : ocsd_isa_thumb2; |
| m_curr_packet.UpdateISA(isa); |
| |
| // check cycle count required - not if reason == 0; |
| m_needCycleCount = (reason != 0) ? m_config->enaCycleAcc() : false; |
| m_gotCycleCount = false; |
| m_numPktBytesReq += (m_needCycleCount ? 1 : 0); |
| m_gotCCBytes = 0; |
| |
| } |
| else if(pktIndex > 5) |
| { |
| // cycle count appears first if present |
| if(m_needCycleCount && !m_gotCycleCount) |
| { |
| if(pktIndex == 6) |
| m_gotCycleCount = (bool)((currByte & 0x40) == 0); // no cont bit, got cycle count |
| else |
| m_gotCycleCount = ((currByte & 0x80) == 0) || (pktIndex == 10); |
| |
| m_gotCCBytes++; // count the cycle count bytes for later use. |
| if(!m_gotCycleCount) // need more cycle count bytes |
| m_numPktBytesReq++; |
| } |
| // then context ID if present. |
| else if( m_numCtxtIDBytes > m_gotCtxtIDBytes) |
| { |
| m_gotCtxtIDBytes++; |
| } |
| } |
| |
| // check if we have enough bytes |
| bGotBytes = (bool)((unsigned)m_numPktBytesReq == m_currPacketData.size()); |
| } |
| else |
| validByte = false; // no byte available, exit. |
| } |
| |
| if(bGotBytes) |
| { |
| // extract address value, cycle count and ctxt id. |
| uint32_t cycleCount = 0; |
| uint32_t ctxtID = 0; |
| int optIdx = 6; // start index for optional elements. |
| |
| // address is always full fixed 32 bit value |
| uint32_t address = ((uint32_t)m_currPacketData[1]) & 0xFE; |
| address |= ((uint32_t)m_currPacketData[2]) << 8; |
| address |= ((uint32_t)m_currPacketData[3]) << 16; |
| address |= ((uint32_t)m_currPacketData[4]) << 24; |
| m_curr_packet.UpdateAddress(address,32); |
| |
| if(m_needCycleCount) |
| { |
| extractCycleCount(optIdx,cycleCount); |
| m_curr_packet.SetCycleCount(cycleCount); |
| optIdx+=m_gotCCBytes; |
| } |
| |
| if(m_numCtxtIDBytes) |
| { |
| extractCtxtID(optIdx,ctxtID); |
| m_curr_packet.UpdateContextID(ctxtID); |
| } |
| m_process_state = SEND_PKT; |
| } |
| } |
| |
| void TrcPktProcPtm::pktTrigger() |
| { |
| m_process_state = SEND_PKT; // no payload |
| } |
| |
| void TrcPktProcPtm::pktWPointUpdate() |
| { |
| bool bDone = false; |
| bool bBytesAvail = true; |
| uint8_t currByte = 0; |
| int byteIdx = 0; |
| |
| if(m_currPacketData.size() == 1) |
| { |
| m_gotAddrBytes = false; // flag to indicate got all needed address bytes |
| m_numAddrBytes = 0; // number of address bytes so far - in this case header is not part of the address |
| |
| m_gotExcepBytes = false; // mark as not got all required exception bytes thus far |
| m_numExcepBytes = 0; // 0 read in |
| |
| m_addrPktIsa = ocsd_isa_unknown; // not set by this packet as yet |
| } |
| |
| // collect all the bytes needed |
| while(!bDone && bBytesAvail) |
| { |
| if(readByte(currByte)) |
| { |
| |
| byteIdx = m_currPacketData.size() - 1; |
| if(!m_gotAddrBytes) |
| { |
| // byteIdx for address byte will run from 1 to 5 - first 4 my have continuation or not. |
| if(byteIdx <= 4) |
| { |
| // address bytes 1 - 4; |
| // ISA stays the same |
| if((currByte & 0x80) == 0x00) |
| { |
| // no further bytes |
| m_gotAddrBytes = true; |
| bDone = true; |
| m_gotExcepBytes = true; |
| } |
| } |
| else |
| { |
| // 5th address byte - determine ISA from this. |
| if((currByte & 0x40) == 0x00) |
| m_gotExcepBytes = true; // no exception bytes - mark as done |
| m_gotAddrBytes = true; |
| bDone = m_gotExcepBytes; |
| |
| m_addrPktIsa = ocsd_isa_arm; // assume ARM, but then check |
| if((currByte & 0x20) == 0x20) // bit 5 == 1'b1 - jazelle, bits 4 & 3 part of address. |
| m_addrPktIsa = ocsd_isa_jazelle; |
| else if((currByte & 0x30) == 0x10) // bit [5:4] == 2'b01 - thumb, bit 3 part of address. |
| m_addrPktIsa = ocsd_isa_thumb2; |
| } |
| m_numAddrBytes++; |
| } |
| else if(!m_gotExcepBytes) |
| { |
| // excep byte is actually a WP update byte. |
| m_excepAltISA = ((currByte & 0x40) == 0x40) ? 1 : 0; |
| m_gotExcepBytes = true; |
| m_numExcepBytes++; |
| bDone = true; |
| } |
| } |
| else |
| bBytesAvail = false; |
| } |
| |
| // analyse the bytes to create the packet |
| if(bDone) |
| { |
| // ISA for the packet |
| if(m_addrPktIsa == ocsd_isa_unknown) // unchanged by trace packet |
| m_addrPktIsa = m_curr_packet.getISA(); // same as prev |
| |
| if(m_gotExcepBytes) // may adjust according to alt ISA in exception packet |
| { |
| if((m_addrPktIsa == ocsd_isa_tee) && (m_excepAltISA == 0)) |
| m_addrPktIsa = ocsd_isa_thumb2; |
| else if((m_addrPktIsa == ocsd_isa_thumb2) && (m_excepAltISA == 1)) |
| m_addrPktIsa = ocsd_isa_tee; |
| } |
| m_curr_packet.UpdateISA(m_addrPktIsa); // mark ISA in packet (update changes current and prev to dectect an ISA change). |
| |
| uint8_t total_bits = 0; |
| uint32_t addr_val = extractAddress(1,total_bits); |
| m_curr_packet.UpdateAddress(addr_val,total_bits); |
| m_process_state = SEND_PKT; |
| } |
| } |
| |
| void TrcPktProcPtm::pktIgnore() |
| { |
| m_process_state = SEND_PKT; // no payload |
| } |
| |
| void TrcPktProcPtm::pktCtxtID() |
| { |
| int pktIndex = m_currPacketData.size() - 1; |
| |
| // if at the header, determine how many more bytes we need. |
| if(pktIndex == 0) |
| { |
| m_numCtxtIDBytes = m_config->CtxtIDBytes(); |
| m_gotCtxtIDBytes = 0; |
| } |
| |
| // read the necessary ctxtID bytes from the stream |
| bool bGotBytes = false, bytesAvail = true; |
| uint32_t ctxtID = 0; |
| |
| bGotBytes = m_numCtxtIDBytes == m_gotCtxtIDBytes; |
| while(!bGotBytes & bytesAvail) |
| { |
| bytesAvail = readByte(); |
| if(bytesAvail) |
| m_gotCtxtIDBytes++; |
| bGotBytes = m_numCtxtIDBytes == m_gotCtxtIDBytes; |
| } |
| |
| if(bGotBytes) |
| { |
| if(m_numCtxtIDBytes) |
| { |
| extractCtxtID(1,ctxtID); |
| } |
| m_curr_packet.UpdateContextID(ctxtID); |
| m_process_state = SEND_PKT; |
| } |
| } |
| |
| void TrcPktProcPtm::pktVMID() |
| { |
| uint8_t currByte; |
| |
| // just need a single payload byte... |
| if(readByte(currByte)) |
| { |
| m_curr_packet.UpdateVMID(currByte); |
| m_process_state = SEND_PKT; |
| } |
| } |
| |
| void TrcPktProcPtm::pktAtom() |
| { |
| uint8_t pHdr = m_currPacketData[0]; |
| |
| if(!m_config->enaCycleAcc()) |
| { |
| m_curr_packet.SetAtomFromPHdr(pHdr); |
| m_process_state = SEND_PKT; |
| } |
| else |
| { |
| bool bGotAllPktBytes = false, byteAvail = true; |
| uint8_t currByte = 0; // cycle accurate tracing -> atom + cycle count |
| |
| if(!(pHdr & 0x40)) |
| { |
| // only the header byte present |
| bGotAllPktBytes = true; |
| } |
| else |
| { |
| // up to 4 additional bytes of count data. |
| while(byteAvail && !bGotAllPktBytes) |
| { |
| if(readByte(currByte)) |
| { |
| if(!(currByte & 0x80) || (m_currPacketData.size() == 5)) |
| bGotAllPktBytes = true; |
| } |
| else |
| byteAvail = false; |
| } |
| } |
| |
| // we have all the bytes for a cycle accurate packet. |
| if(bGotAllPktBytes) |
| { |
| uint32_t cycleCount = 0; |
| extractCycleCount(0,cycleCount); |
| m_curr_packet.SetCycleCount(cycleCount); |
| m_curr_packet.SetCycleAccAtomFromPHdr(pHdr); |
| m_process_state = SEND_PKT; |
| } |
| } |
| } |
| |
| void TrcPktProcPtm::pktTimeStamp() |
| { |
| uint8_t currByte = 0; |
| int pktIndex = m_currPacketData.size() - 1; |
| bool bGotBytes = false, byteAvail = true; |
| |
| if(pktIndex == 0) |
| { |
| m_gotTSBytes = false; |
| m_needCycleCount = m_config->enaCycleAcc(); |
| m_gotCCBytes = 0; |
| |
| // max byte buffer size for full ts packet |
| m_tsByteMax = m_config->TSPkt64() ? 10 : 8; |
| } |
| |
| while(byteAvail && !bGotBytes) |
| { |
| if(readByte(currByte)) |
| { |
| if(!m_gotTSBytes) |
| { |
| if(((currByte & 0x80) == 0) || (m_currPacketData.size() == (unsigned)m_tsByteMax)) |
| { |
| m_gotTSBytes = true; |
| if(!m_needCycleCount) |
| bGotBytes = true; |
| } |
| } |
| else |
| { |
| uint8_t cc_cont_mask = 0x80; |
| // got TS bytes, collect cycle count |
| if(m_gotCCBytes == 0) |
| cc_cont_mask = 0x40; |
| if((currByte & cc_cont_mask) == 0) |
| bGotBytes = true; |
| m_gotCCBytes++; |
| if(m_gotCCBytes == 5) |
| bGotBytes = true; |
| } |
| } |
| else |
| byteAvail = false; |
| } |
| |
| if(bGotBytes) |
| { |
| uint64_t tsVal = 0; |
| uint32_t cycleCount = 0; |
| uint8_t tsUpdateBits = 0; |
| int ts_end_idx = extractTS(tsVal,tsUpdateBits); |
| if(m_needCycleCount) |
| { |
| extractCycleCount(ts_end_idx,cycleCount); |
| m_curr_packet.SetCycleCount(cycleCount); |
| } |
| m_curr_packet.UpdateTimestamp(tsVal,tsUpdateBits); |
| m_process_state = SEND_PKT; |
| } |
| } |
| |
| void TrcPktProcPtm::pktExceptionRet() |
| { |
| m_process_state = SEND_PKT; // no payload |
| } |
| |
| void TrcPktProcPtm::pktBranchAddr() |
| { |
| uint8_t currByte = m_currPacketData[0]; |
| bool bDone = false; |
| bool bBytesAvail = true; |
| int byteIdx = 0; |
| |
| if(m_currPacketData.size() == 1) |
| { |
| m_gotAddrBytes = false; // flag to indicate got all needed address bytes |
| m_numAddrBytes = 1; // number of address bytes so far |
| |
| m_needCycleCount = m_config->enaCycleAcc(); // check if we have a cycle count |
| m_gotCCBytes = 0; // number of cc bytes read in so far. |
| |
| m_gotExcepBytes = false; // mark as not got all required exception bytes thus far |
| m_numExcepBytes = 0; // 0 read in |
| |
| m_addrPktIsa = ocsd_isa_unknown; // not set by this packet as yet |
| |
| // header is also 1st address byte |
| if((currByte & 0x80) == 0) // could be single byte packet |
| { |
| m_gotAddrBytes = true; |
| if(!m_needCycleCount) |
| bDone = true; // all done if no cycle count |
| m_gotExcepBytes = true; // cannot have exception bytes following single byte packet |
| } |
| |
| } |
| |
| // collect all the bytes needed |
| while(!bDone && bBytesAvail) |
| { |
| if(readByte(currByte)) |
| { |
| byteIdx = m_currPacketData.size() - 1; |
| if(!m_gotAddrBytes) |
| { |
| if(byteIdx < 4) |
| { |
| // address bytes 2 - 4; |
| // ISA stays the same |
| if((currByte & 0x80) == 0x00) |
| { |
| // no further bytes |
| if((currByte & 0x40) == 0x00) |
| m_gotExcepBytes = true; // no exception bytes - mark as done |
| m_gotAddrBytes = true; |
| bDone = m_gotExcepBytes && !m_needCycleCount; |
| } |
| } |
| else |
| { |
| // 5th address byte - determine ISA from this. |
| if((currByte & 0x40) == 0x00) |
| m_gotExcepBytes = true; // no exception bytes - mark as done |
| m_gotAddrBytes = true; |
| bDone = m_gotExcepBytes && !m_needCycleCount; |
| |
| m_addrPktIsa = ocsd_isa_arm; // assume ARM, but then check |
| if((currByte & 0x20) == 0x20) // bit 5 == 1'b1 - jazelle, bits 4 & 3 part of address. |
| m_addrPktIsa = ocsd_isa_jazelle; |
| else if((currByte & 0x30) == 0x10) // bit [5:4] == 2'b01 - thumb, bit 3 part of address. |
| m_addrPktIsa = ocsd_isa_thumb2; |
| } |
| m_numAddrBytes++; |
| } |
| else if(!m_gotExcepBytes) |
| { |
| // may need exception bytes |
| if(m_numExcepBytes == 0) |
| { |
| if((currByte & 0x80) == 0x00) |
| m_gotExcepBytes = true; |
| m_excepAltISA = ((currByte & 0x40) == 0x40) ? 1 : 0; |
| } |
| else |
| m_gotExcepBytes = true; |
| m_numExcepBytes++; |
| |
| if(m_gotExcepBytes && !m_needCycleCount) |
| bDone = true; |
| |
| } |
| else if(m_needCycleCount) |
| { |
| // not done after exception bytes, collect cycle count |
| if(m_gotCCBytes == 0) |
| { |
| bDone = ((currByte & 0x40) == 0x00 ); |
| } |
| else |
| { |
| // done if no more or 5th byte |
| bDone = (((currByte & 0x80) == 0x00 ) || (m_gotCCBytes == 4)); |
| } |
| m_gotCCBytes++; |
| } |
| else |
| // this should never be reached. |
| throwMalformedPacketErr("sequencing error analysing branch packet"); |
| } |
| else |
| bBytesAvail = false; |
| } |
| |
| // analyse the bytes to create the packet |
| if(bDone) |
| { |
| // ISA for the packet |
| if(m_addrPktIsa == ocsd_isa_unknown) // unchanged by trace packet |
| m_addrPktIsa = m_curr_packet.getISA(); // same as prev |
| |
| if(m_gotExcepBytes) // may adjust according to alt ISA in exception packet |
| { |
| if((m_addrPktIsa == ocsd_isa_tee) && (m_excepAltISA == 0)) |
| m_addrPktIsa = ocsd_isa_thumb2; |
| else if((m_addrPktIsa == ocsd_isa_thumb2) && (m_excepAltISA == 1)) |
| m_addrPktIsa = ocsd_isa_tee; |
| } |
| m_curr_packet.UpdateISA(m_addrPktIsa); // mark ISA in packet (update changes current and prev to dectect an ISA change). |
| |
| |
| |
| // we know the ISA, we can correctly interpret the address. |
| uint8_t total_bits = 0; |
| uint32_t addr_val = extractAddress(0,total_bits); |
| m_curr_packet.UpdateAddress(addr_val,total_bits); |
| |
| if(m_numExcepBytes > 0) |
| { |
| uint8_t E1 = m_currPacketData[m_numAddrBytes]; |
| uint16_t ENum = (E1 >> 1) & 0xF; |
| ocsd_armv7_exception excep = Excp_Reserved; |
| |
| m_curr_packet.UpdateNS(E1 & 0x1); |
| if(m_numExcepBytes > 1) |
| { |
| uint8_t E2 = m_currPacketData[m_numAddrBytes+1]; |
| m_curr_packet.UpdateHyp((E2 >> 5) & 0x1); |
| ENum |= ((uint16_t)(E2 & 0x1F) << 4); |
| } |
| |
| if(ENum <= 0xF) |
| { |
| static ocsd_armv7_exception v7ARExceptions[16] = { |
| Excp_NoException, Excp_DebugHalt, Excp_SMC, Excp_Hyp, |
| Excp_AsyncDAbort, Excp_ThumbEECheckFail, Excp_Reserved, Excp_Reserved, |
| Excp_Reset, Excp_Undef, Excp_SVC, Excp_PrefAbort, |
| Excp_SyncDataAbort, Excp_Generic, Excp_IRQ, Excp_FIQ |
| }; |
| excep = v7ARExceptions[ENum]; |
| } |
| m_curr_packet.SetException(excep,ENum); |
| } |
| |
| if(m_needCycleCount) |
| { |
| int countIdx = m_numAddrBytes + m_numExcepBytes; |
| uint32_t cycleCount = 0; |
| extractCycleCount(countIdx,cycleCount); |
| m_curr_packet.SetCycleCount(cycleCount); |
| } |
| m_process_state = SEND_PKT; |
| } |
| } |
| |
| void TrcPktProcPtm::pktReserved() |
| { |
| m_process_state = SEND_PKT; // no payload |
| } |
| |
| void TrcPktProcPtm::extractCtxtID(int idx, uint32_t &ctxtID) |
| { |
| ctxtID = 0; |
| int shift = 0; |
| for(int i=0; i < m_numCtxtIDBytes; i++) |
| { |
| if((size_t)idx+i >= m_currPacketData.size()) |
| throwMalformedPacketErr("Insufficient packet bytes for Context ID value."); |
| ctxtID |= ((uint32_t)m_currPacketData[idx+i]) << shift; |
| shift+=8; |
| } |
| } |
| |
| void TrcPktProcPtm::extractCycleCount(int offset, uint32_t &cycleCount) |
| { |
| bool bCont = true; |
| cycleCount = 0; |
| int by_idx = 0; |
| uint8_t currByte; |
| int shift = 4; |
| |
| while(bCont) |
| { |
| if((size_t)by_idx+offset >= m_currPacketData.size()) |
| throwMalformedPacketErr("Insufficient packet bytes for Cycle Count value."); |
| |
| currByte = m_currPacketData[offset+by_idx]; |
| if(by_idx == 0) |
| { |
| bCont = (currByte & 0x40) != 0; |
| cycleCount = (currByte >> 2) & 0xF; |
| } |
| else |
| { |
| |
| bCont = (currByte & 0x80) != 0; |
| if(by_idx == 4) |
| bCont = false; |
| cycleCount |= (((uint32_t)(currByte & 0x7F)) << shift); |
| shift += 7; |
| } |
| by_idx++; |
| } |
| } |
| |
| int TrcPktProcPtm::extractTS(uint64_t &tsVal,uint8_t &tsUpdateBits) |
| { |
| bool bCont = true; |
| int tsIdx = 1; // start index; |
| uint8_t byteVal; |
| bool b64BitVal = m_config->TSPkt64(); |
| int shift = 0; |
| |
| tsVal = 0; |
| tsUpdateBits = 0; |
| |
| while(bCont) |
| { |
| if((size_t)tsIdx >= m_currPacketData.size()) |
| throwMalformedPacketErr("Insufficient packet bytes for Timestamp value."); |
| |
| byteVal = m_currPacketData[tsIdx]; |
| |
| if(b64BitVal) |
| { |
| if(tsIdx < 9) |
| { |
| bCont = ((byteVal & 0x80) == 0x80); |
| byteVal &= 0x7F; |
| tsUpdateBits += 7; |
| } |
| else |
| { |
| bCont = false; |
| tsUpdateBits += 8; |
| } |
| } |
| else |
| { |
| if(tsIdx < 7) |
| { |
| bCont = ((byteVal & 0x80) == 0x80); |
| byteVal &= 0x7F; |
| tsUpdateBits += 7; |
| } |
| else |
| { |
| byteVal &=0x3F; |
| bCont = false; |
| tsUpdateBits += 6; |
| } |
| } |
| tsVal |= (((uint64_t)byteVal) << shift); |
| tsIdx++; |
| shift += 7; |
| } |
| return tsIdx; // return next byte index in packet. |
| } |
| |
| uint32_t TrcPktProcPtm::extractAddress(const int offset, uint8_t &total_bits) |
| { |
| // we know the ISA, we can correctly interpret the address. |
| uint32_t addr_val = 0; |
| uint8_t mask = 0x7E; // first byte mask (always); |
| uint8_t num_bits = 0x7; // number of bits in the 1st byte (thumb); |
| int shift = 0; |
| int next_shift = 0; |
| |
| total_bits = 0; |
| |
| for(int i = 0; i < m_numAddrBytes; i++) |
| { |
| if(i == 4) |
| { |
| // 5th byte mask |
| mask = 0x0f; // thumb mask; |
| num_bits = 4; |
| if(m_addrPktIsa == ocsd_isa_jazelle) |
| { |
| mask = 0x1F; |
| num_bits = 5; |
| } |
| else if(m_addrPktIsa == ocsd_isa_arm) |
| { |
| mask = 0x07; |
| num_bits = 3; |
| } |
| } |
| else if(i > 0) |
| { |
| mask = 0x7F; |
| num_bits = 7; |
| // check for last byte but not 1st or 5th byte mask |
| if(i == m_numAddrBytes-1) |
| { |
| mask = 0x3F; |
| num_bits = 6; |
| } |
| } |
| |
| // extract data |
| shift = next_shift; |
| addr_val |= ((uint32_t)(m_currPacketData[i+offset] & mask) << shift); |
| total_bits += num_bits; |
| |
| // how much we shift the next value |
| if(i == 0) |
| { |
| if(m_addrPktIsa == ocsd_isa_jazelle) |
| { |
| addr_val >>= 1; |
| next_shift = 6; |
| total_bits--; // adjust bits for jazelle offset |
| } |
| else |
| { |
| next_shift = 7; |
| } |
| } |
| else |
| { |
| next_shift += 7; |
| } |
| } |
| |
| if(m_addrPktIsa == ocsd_isa_arm) |
| { |
| addr_val <<= 1; // shift one extra bit for ARM address alignment. |
| total_bits++; |
| } |
| return addr_val; |
| } |
| |
| |
| void TrcPktProcPtm::BuildIPacketTable() |
| { |
| // initialise all to branch, atom or reserved packet header |
| for(unsigned i = 0; i < 256; i++) |
| { |
| // branch address packets all end in 8'bxxxxxxx1 |
| if((i & 0x01) == 0x01) |
| { |
| m_i_table[i].pkt_type = PTM_PKT_BRANCH_ADDRESS; |
| m_i_table[i].pptkFn = &TrcPktProcPtm::pktBranchAddr; |
| } |
| // atom packets are 8'b1xxxxxx0 |
| else if((i & 0x81) == 0x80) |
| { |
| m_i_table[i].pkt_type = PTM_PKT_ATOM; |
| m_i_table[i].pptkFn = &TrcPktProcPtm::pktAtom; |
| } |
| else |
| { |
| // set all the others to reserved for now |
| m_i_table[i].pkt_type = PTM_PKT_RESERVED; |
| m_i_table[i].pptkFn = &TrcPktProcPtm::pktReserved; |
| } |
| } |
| |
| // pick out the other packet types by individual codes. |
| |
| // A-sync 8'b00000000 |
| m_i_table[0x00].pkt_type = PTM_PKT_A_SYNC; |
| m_i_table[0x00].pptkFn = &TrcPktProcPtm::pktASync; |
| |
| // I-sync 8'b00001000 |
| m_i_table[0x08].pkt_type = PTM_PKT_I_SYNC; |
| m_i_table[0x08].pptkFn = &TrcPktProcPtm::pktISync; |
| |
| // waypoint update 8'b01110010 |
| m_i_table[0x72].pkt_type = PTM_PKT_WPOINT_UPDATE; |
| m_i_table[0x72].pptkFn = &TrcPktProcPtm::pktWPointUpdate; |
| |
| // trigger 8'b00001100 |
| m_i_table[0x0C].pkt_type = PTM_PKT_TRIGGER; |
| m_i_table[0x0C].pptkFn = &TrcPktProcPtm::pktTrigger; |
| |
| // context ID 8'b01101110 |
| m_i_table[0x6E].pkt_type = PTM_PKT_CONTEXT_ID; |
| m_i_table[0x6E].pptkFn = &TrcPktProcPtm::pktCtxtID; |
| |
| // VMID 8'b00111100 |
| m_i_table[0x3C].pkt_type = PTM_PKT_VMID; |
| m_i_table[0x3C].pptkFn = &TrcPktProcPtm::pktVMID; |
| |
| // Timestamp 8'b01000x10 |
| m_i_table[0x42].pkt_type = PTM_PKT_TIMESTAMP; |
| m_i_table[0x42].pptkFn = &TrcPktProcPtm::pktTimeStamp; |
| m_i_table[0x46].pkt_type = PTM_PKT_TIMESTAMP; |
| m_i_table[0x46].pptkFn = &TrcPktProcPtm::pktTimeStamp; |
| |
| // Exception return 8'b01110110 |
| m_i_table[0x76].pkt_type = PTM_PKT_EXCEPTION_RET; |
| m_i_table[0x76].pptkFn = &TrcPktProcPtm::pktExceptionRet; |
| |
| // Ignore 8'b01100110 |
| m_i_table[0x66].pkt_type = PTM_PKT_IGNORE; |
| m_i_table[0x66].pptkFn = &TrcPktProcPtm::pktIgnore; |
| } |
| |
| /* End of File trc_pkt_proc_ptm.cpp */ |