blob: 68cc58e22887a356e7a952e34d1e7c1842ef2390 [file]
/*
* \file trc_pkt_proc_itm.cpp
* \brief OpenCSD :
*
* \copyright Copyright (c) 2024, 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/itm/trc_pkt_proc_itm.h"
// processor object construction
// ************************
#ifdef __GNUC__
// G++ doesn't like the ## pasting
#define ITM_PKTS_NAME "PKTP_ITM"
#else
#define ITM_PKTS_NAME OCSD_CMPNAME_PREFIX_PKTPROC##"_ITM"
#endif
static const uint32_t ITM_SUPPORTED_OP_FLAGS = OCSD_OPFLG_PKTPROC_COMMON;
TrcPktProcItm::TrcPktProcItm() : TrcPktProcBase(ITM_PKTS_NAME)
{
initObj();
}
TrcPktProcItm::TrcPktProcItm(int instIDNum) : TrcPktProcBase(ITM_PKTS_NAME, instIDNum)
{
initObj();
}
TrcPktProcItm::~TrcPktProcItm()
{
getRawPacketMonAttachPt()->set_notifier(0);
}
void TrcPktProcItm::initObj()
{
m_supported_op_flags = ITM_SUPPORTED_OP_FLAGS;
initProcessorState();
}
void TrcPktProcItm::initProcessorState()
{
// clear any state that persists between packets
setProcUnsynced();
initNextPacket();
m_sent_notsync_packet = false;
m_sync_start = false;
m_dump_unsynced_bytes = 0;
}
void TrcPktProcItm::initNextPacket()
{
// clear state that is unique to each packet
m_packet_data.clear();
m_curr_packet.initPacket();
}
// implementation packet processing interface overrides
// ************************
ocsd_datapath_resp_t TrcPktProcItm::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;
m_p_data_in = pDataBlock;
m_data_in_size = dataBlockSize;
m_data_in_used = 0;
// while there is data and a continue response on the data path
while (dataToProcess() && OCSD_DATA_RESP_IS_CONT(resp))
{
try
{
switch (m_proc_state)
{
case WAIT_SYNC:
resp = waitForSync(index);
break;
case PROC_HDR:
m_packet_index = index + m_data_in_used;
itmProcessHdr(); // will set to PROC_DATA or SEND_PKT on valid header.
// if we are not now in data processing, break, otherwise fall through
if (m_proc_state != PROC_DATA)
break;
case PROC_DATA:
(this->*m_pCurrPktFn)();
// if we have enough to send, fall through, otherwise stop
if (m_proc_state != SEND_PKT)
break;
case SEND_PKT:
resp = outputPacket();
break;
}
}
catch (ocsdError& err)
{
LogError(err);
if (((err.getErrorCode() == OCSD_ERR_BAD_PACKET_SEQ) ||
(err.getErrorCode() == OCSD_ERR_INVALID_PCKT_HDR)) &&
!(getComponentOpMode() & OCSD_OPFLG_PKTPROC_ERR_BAD_PKTS))
{
// send invalid packets up the pipe to let the next stage decide what to do.
resp = outputPacket();
if (getComponentOpMode() & OCSD_OPFLG_PKTPROC_UNSYNC_ON_BAD_PKTS)
m_proc_state = WAIT_SYNC;
}
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;
ocsdError fatal = ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_FAIL, m_packet_index, m_config->getTraceID());
fatal.setMessage("Unknown System Error decoding trace.");
LogError(fatal);
}
}
*numBytesProcessed = m_data_in_used;
return resp;
}
ocsd_datapath_resp_t TrcPktProcItm::onEOT()
{
ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
if (m_proc_state == PROC_DATA) // there is a partial packet in flight
{
m_curr_packet.updateErrType(ITM_PKT_INCOMPLETE_EOT); // re mark as incomplete
resp = outputPacket();
}
return resp;
}
ocsd_datapath_resp_t TrcPktProcItm::onReset()
{
initProcessorState();
return OCSD_RESP_CONT;
}
ocsd_datapath_resp_t TrcPktProcItm::onFlush()
{
// packet processor never holds on to flushable data (may have partial packet,
// but any full packets are immediately sent)
return OCSD_RESP_CONT;
}
ocsd_err_t TrcPktProcItm::onProtocolConfig()
{
return OCSD_OK; // nothing to do on config for this processor
}
const bool TrcPktProcItm::isBadPacket() const
{
return m_curr_packet.isBadPacket();
}
ocsd_datapath_resp_t TrcPktProcItm::outputPacket()
{
ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
resp = outputOnAllInterfaces(m_packet_index, &m_curr_packet, &m_curr_packet.type, m_packet_data);
m_packet_data.clear();
initNextPacket();
m_proc_state = m_bStreamSync ? PROC_HDR : WAIT_SYNC;
return resp;
}
void TrcPktProcItm::throwBadSequenceError(const char* pszMessage /*= ""*/)
{
m_curr_packet.updateErrType(ITM_PKT_BAD_SEQUENCE);
throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_BAD_PACKET_SEQ, m_packet_index, this->m_config->getTraceID(), pszMessage);
}
void TrcPktProcItm::throwReservedHdrError(const char* pszMessage /*= ""*/)
{
m_curr_packet.setPktType(ITM_PKT_RESERVED);
throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_INVALID_PCKT_HDR, m_packet_index, this->m_config->getTraceID(), pszMessage);
}
/**************/
/* packet processing routines */
const bool TrcPktProcItm::readByte(uint8_t &byte)
{
bool gotData = true;
if (m_data_in_used < m_data_in_size)
{
byte = m_p_data_in[m_data_in_used++];
savePacketByte(byte);
}
else
gotData = false;
return gotData;
}
void TrcPktProcItm::itmProcessHdr()
{
// no data, just return and fall out of processing routine.
if (!readByte(m_header_byte))
return;
if ((m_header_byte & 0x03) != 0x00)
{
// Stimulus data packets
if (m_header_byte & 0x4)
m_curr_packet.setPktType(ITM_PKT_DWT);
else
m_curr_packet.setPktType(ITM_PKT_SWIT);
m_pCurrPktFn = &TrcPktProcItm::itmPktData;
m_proc_state = PROC_DATA;
}
else if ((m_header_byte & 0x0F) == 0x00)
{
// bottom 4 bits 0b0000 - ASYNC, OVERFLOW or Local TS
if ((m_header_byte & 0xF0) == 0x00)
{
// ASYNC
m_curr_packet.setPktType(ITM_PKT_ASYNC);
m_pCurrPktFn = &TrcPktProcItm::itmPktAsync;
m_proc_state = PROC_DATA;
}
else if ((m_header_byte & 0xF0) == 0x70)
{
// Overflow
m_curr_packet.setPktType(ITM_PKT_OVERFLOW);
m_proc_state = SEND_PKT;
}
else
{
// LOCAL TS
m_curr_packet.setPktType(ITM_PKT_TS_LOCAL);
m_pCurrPktFn = &TrcPktProcItm::itmPktLocalTS;
m_proc_state = PROC_DATA;
}
}
else if ((m_header_byte & 0x0B) == 0x08)
{
// looking for extension packet 0b xxxx 1x00
m_curr_packet.setPktType(ITM_PKT_EXTENSION);
m_pCurrPktFn = &TrcPktProcItm::itmPktExtension;
m_proc_state = PROC_DATA;
}
else if ((m_header_byte & 0xDF) == 0x94)
{
// looking for global ts packet 0b 10x1 0100 -> mask = 8b 1101 1111
if ((m_header_byte & 0x20) == 0x00)
{
m_curr_packet.setPktType(ITM_PKT_TS_GLOBAL_1);
m_pCurrPktFn = &TrcPktProcItm::itmPktGlobalTS1;
}
else
{
m_curr_packet.setPktType(ITM_PKT_TS_GLOBAL_2);
m_pCurrPktFn = &TrcPktProcItm::itmPktGlobalTS2;
}
m_proc_state = PROC_DATA;
}
else
throwReservedHdrError();
}
void TrcPktProcItm::itmPktData()
{
uint8_t byte;
uint32_t value;
int payload_bytes_req = m_header_byte & 0x3;
int payload_bytes_got = (int)(m_packet_data.size() - 1);
if (payload_bytes_req == 3)
payload_bytes_req = 4;
// got a SWIT or DWT header - process data according to size
if (m_packet_data.size() == 1)
{
// save the stimulus address / HW src descriminator - 5 bits
m_curr_packet.setSrcID((m_header_byte >> 3) & 0x1F);
}
// loop to get payload bytes
while (payload_bytes_got < payload_bytes_req)
{
// readbyte will put data into the array
if (!readByte(byte))
break;
payload_bytes_got++;
}
if (payload_bytes_got == payload_bytes_req)
{
value = (uint32_t)m_packet_data[1];
if (payload_bytes_req >= 2)
value |= (((uint32_t)m_packet_data[2]) << 8);
if (payload_bytes_req == 4)
{
value |= (((uint32_t)m_packet_data[3]) << 16);
value |= (((uint32_t)m_packet_data[4]) << 24);
}
m_curr_packet.setValue(value, payload_bytes_req);
m_proc_state = SEND_PKT;
}
}
// read bytes with continue bit into buffer. Return true if last byte seen, false if out of data.
// limit is set to max bytes in packet - stop overrun on data errors
bool TrcPktProcItm::readContBytes(uint32_t limit)
{
bool bDone = false;
uint8_t byte;
while (!bDone && (m_packet_data.size() < limit))
{
if (!readByte(byte))
break;
bDone = ((byte & 0x80) == 0x00);
}
return bDone;
}
uint32_t TrcPktProcItm::extractContVal32()
{
uint32_t value = 0;
int shift = 0;
int idx, idx_max = (int)m_packet_data.size() - 1;
for (idx = 1, shift = 0; idx <= idx_max; idx++, shift += 7)
value |= (((uint32_t)m_packet_data[idx] & 0x7F) << shift);
return value;
}
uint64_t TrcPktProcItm::extractContVal64()
{
uint64_t value = 0;
int shift = 0;
int idx, idx_max = (int)m_packet_data.size() - 1;
for (idx = 1, shift = 0; idx <= idx_max; idx++, shift += 7)
value |= (((uint64_t)m_packet_data[idx] & 0x7F) << shift);
return value;
}
void TrcPktProcItm::itmPktLocalTS()
{
bool bGotContVal = false;
const int pkt_size_limit = 5; // header + up to 4 payload bytes
if (m_packet_data.size() == 1)
{
// set the TC value into the src_id parameter - if cont bit set
if (m_header_byte & 0x80)
m_curr_packet.setSrcID((m_header_byte >> 4) & 0x3);
else
{
// otherwise a single byte Local TS - value in header, TS_SYNC (TC = 2b00) packet
m_curr_packet.setSrcID(0);
m_curr_packet.setValue((m_header_byte >> 4) & 0x7, 1);
m_proc_state = SEND_PKT;
return;
}
}
bGotContVal = readContBytes(pkt_size_limit);
if (bGotContVal)
{
m_curr_packet.setValue(extractContVal32(), (int)m_packet_data.size() - 1);
m_proc_state = SEND_PKT;
}
else if (m_packet_data.size() == pkt_size_limit)
{
throwBadSequenceError("Local TS packet: Payload continuation value too long");
}
}
void TrcPktProcItm::itmPktGlobalTS1()
{
bool bGotContVal = false;
const int pkt_size_limit = 5; // header + up to 4 payload bytes
uint8_t byte;
// nothing to process from the header - just do cont payload.
bGotContVal = readContBytes(pkt_size_limit);
if (bGotContVal)
{
// if we have a fourth payload byte - this contains additional data.
if (m_packet_data.size() == 5)
{
byte = m_packet_data[4];
// bits [6:5] are wrap and clk change - move these to src_id
m_curr_packet.setSrcID(((byte >> 5) & 0x3));
// clear those bits from the data before extracting thevalue
m_packet_data[4] = byte & 0x1F;
}
m_curr_packet.setValue(extractContVal32(), (int)m_packet_data.size() - 1);
m_proc_state = SEND_PKT;
}
else if (m_packet_data.size() == pkt_size_limit)
{
throwBadSequenceError("GTS1 packet: Payload continuation value too long");
}
}
void TrcPktProcItm::itmPktGlobalTS2()
{
bool bGotContVal = false;
const int pkt_size_limit = 7; // header + up to 6 payload bytes
// nothing to process from the header - just do cont payload.
bGotContVal = readContBytes(pkt_size_limit);
if (bGotContVal)
{
if (m_packet_data.size() <= 5)
m_curr_packet.setValue(extractContVal32(), (int)m_packet_data.size() - 1);
else
m_curr_packet.setExtValue(extractContVal64());
m_proc_state = SEND_PKT;
}
else if (m_packet_data.size() == pkt_size_limit)
{
throwBadSequenceError("GTS2 packet: Payload continuation value too long");
}
}
void TrcPktProcItm::itmPktExtension()
{
bool bGotContVal = false;
const int pkt_size_limit = 5; // header + up to 4 payload bytes
const uint8_t N_bit_length[] = { 2, 9, 16, 23, 31 }; // bitlengths for payload.
uint8_t src_id_val = 0;
uint32_t value = 0;
// we can have just the header..
if ((m_header_byte & 0x80) == 0)
bGotContVal = true;
else
bGotContVal = readContBytes(pkt_size_limit);
if (bGotContVal)
{
// put bit length and information source bit into src_id
// [7] = SH (software - 0b0 / hardware - 0b1) source
// [4:0] = max N bit index of payload.
src_id_val = N_bit_length[m_packet_data.size() - 1];
if (m_header_byte & 0x4)
src_id_val |= 0x80;
m_curr_packet.setSrcID(src_id_val);
// now set the value
if (m_packet_data.size() > 1)
{
value = extractContVal32();
value <<= 3;
}
value |= (uint32_t)((m_header_byte >> 4) & 0x7);
m_curr_packet.setValue(value, 4);
m_proc_state = SEND_PKT;
}
else if (m_packet_data.size() == pkt_size_limit)
{
throwBadSequenceError("Extension packet: Payload continuation value too long");
}
}
bool TrcPktProcItm::readAsyncSeq(bool &bError)
{
uint8_t byte;
bool bFoundAsync = false;
bError = false;
// at least 5 0x00 packets - non zero an error
while ((m_packet_data.size() < 5) && !bError)
{
if (!readByte(byte))
break;
if (byte != 0x00)
bError = true;
}
// now can be more 0x00 before a 0x80 value - other non zero an error.
while (!bFoundAsync && !bError)
{
if (!readByte(byte))
break;
if (byte == 0x80)
bFoundAsync = true;
else if (byte != 0x00)
bError = true;
}
return bFoundAsync;
}
// processing an async packet when synchronised - see waitForSync for processing data to search for first async.
void TrcPktProcItm::itmPktAsync()
{
bool bFoundAsync = false, bError = false;
bFoundAsync = readAsyncSeq(bError);
if (bFoundAsync)
m_proc_state = SEND_PKT;
else if (bError)
throwBadSequenceError("Async Packet: unexpected none zero value");
}
ocsd_datapath_resp_t TrcPktProcItm::waitForSync(const ocsd_trc_index_t blk_st_index)
{
ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
uint8_t byte;
bool bAsyncErr;
m_curr_packet.setPktType(ITM_PKT_NOTSYNC);
m_dump_unsynced_bytes = 0;
// not currently processing a possible async sequence so set index to start of current input data.
if (!m_sync_start)
m_packet_index = blk_st_index + m_data_in_used;
while (!m_bStreamSync && dataToProcess() && OCSD_DATA_RESP_IS_CONT(resp))
{
if (m_sync_start)
{
// continue look for sync pattern in data
m_bStreamSync = readAsyncSeq(bAsyncErr);
if (m_bStreamSync)
{
m_curr_packet.setPktType(ITM_PKT_ASYNC);
m_proc_state = SEND_PKT;
}
else if (bAsyncErr)
{
// not found as not async pattern - back to hunt for start of async packet
m_dump_unsynced_bytes = (int)m_packet_data.size();
m_sync_start = false;
}
}
if (!m_sync_start)
{
if (!readByte(byte))
break;
if (byte == 0x00)
{
// potential async header
m_sync_start = true;
// get rid of any preceding bytes
resp = flushUnsyncedBytes();
// set index to the possible async header
m_packet_index = blk_st_index + m_data_in_used - 1;
}
else
{
m_dump_unsynced_bytes++;
// periodically flush unsynced bytes during search.
if (m_dump_unsynced_bytes >= 8)
resp = flushUnsyncedBytes();
}
}
}
// not found sync and run out of data
if (!m_bStreamSync && !m_sync_start)
resp = flushUnsyncedBytes();
return resp;
}
ocsd_datapath_resp_t TrcPktProcItm::flushUnsyncedBytes()
{
ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
// we need to dump unsynced bytes to raw monitor if in use, and send a not sync packet if this is the first time.
outputRawPacketToMonitor(m_packet_index, &m_curr_packet, m_dump_unsynced_bytes, &m_packet_data[0]);
if (!m_sent_notsync_packet)
{
resp = outputDecodedPacket(m_packet_index, &m_curr_packet);
m_sent_notsync_packet = true;
}
if (m_packet_data.size() <= (uint32_t)m_dump_unsynced_bytes)
m_packet_data.clear();
else
m_packet_data.erase(m_packet_data.begin(), m_packet_data.begin() + m_dump_unsynced_bytes);
m_dump_unsynced_bytes = 0;
return resp;
}