1 /*
2 * \file ext_dcd_echo_test_fact.c
3 * \brief OpenCSD : Echo test custom decoder factory
4 *
5 * \copyright Copyright (c) 2016, ARM Limited. All Rights Reserved.
6 */
7
8 /*
9 * Redistribution and use in source and binary forms, with or without modification,
10 * are permitted provided that the following conditions are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
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.
18 *
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.
22 *
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.
33 */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "opencsd/c_api/opencsd_c_api.h"
40 #include "opencsd/c_api/ocsd_c_api_types.h"
41 #include "opencsd/c_api/ocsd_c_api_cust_impl.h"
42
43 #include "ext_dcd_echo_test_fact.h"
44 #include "ext_dcd_echo_test.h"
45
46 /** The name of the decoder */
47 #define DECODER_NAME "ECHO_TEST"
48
49 /********* External callback fns passed to library *****/
50 /** Declare the trace data input function for the decoder - passed to library as call-back. */
51 static ocsd_datapath_resp_t echo_dcd_trace_data_in(const void *decoder_handle,
52 const ocsd_datapath_op_t op,
53 const ocsd_trc_index_t index,
54 const uint32_t dataBlockSize,
55 const uint8_t *pDataBlock,
56 uint32_t *numBytesProcessed);
57
58 /** Allow library to update the packet monitor / sink in use flags to allow decoder to call CB as appropriate.*/
59 static void echo_dcd_update_mon_flags(const void *decoder_handle, const int flags);
60
61 /********* Fns called by decoder creation factory *****/
62 void echo_dcd_init(echo_decoder_t *decoder,
63 ocsd_extern_dcd_inst_t *p_decoder_inst,
64 const echo_dcd_cfg_t *p_config,
65 const ocsd_extern_dcd_cb_fns *p_lib_callbacks);
66
67 void echo_dcd_pkt_tostr(echo_dcd_pkt_t *pkt, char *buffer, const int buflen);
68
69 /********* Internal decoder functions *****/
70 static void echo_dcd_reset(echo_decoder_t *decoder);
71 static ocsd_datapath_resp_t echo_dcd_process_data(echo_decoder_t *decoder,
72 const ocsd_trc_index_t index,
73 const uint32_t dataBlockSize,
74 const uint8_t *pDataBlock,
75 uint32_t *numBytesProcessed);
76
77 static ocsd_datapath_resp_t send_gen_packet(echo_decoder_t *decoder);
78 static ocsd_datapath_resp_t analyse_packet(echo_decoder_t *decoder);
79 static ocsd_datapath_resp_t send_none_data_op(echo_decoder_t *decoder, const ocsd_datapath_op_t op);
80 static void print_init_test_message(echo_decoder_t *decoder);
81
82 /******Infrastructure testing functionality *********************/
83 /* As this is a test decoder we want to check which of the callbacks or call-ins are covered for a given test run
84 (by definition they can't all be - so will need a couple of runs to test all)
85 */
86 enum {
87 TEST_COV_ERRORLOG_CB = 0,
88 TEST_COV_MSGLOG_CB,
89 TEST_COV_GEN_ELEM_CB,
90 TEST_COV_IDEC_CB,
91 TEST_COV_MEM_ACC_CB,
92 TEST_COV_PKTMON_CB,
93 TEST_COV_PKTSINK_CB,
94 TEST_COV_INDATA,
95 TEST_COV_INCBFLAGS,
96 /**/
97 TEST_COV_END
98 };
99
100 typedef enum {
101 TEST_RES_NA, /* not tested */
102 TEST_RES_OK, /* test OK */
103 TEST_RES_FAIL /* test fail */
104 } test_result_t;
105
106 static test_result_t coverage[TEST_COV_END] = { TEST_RES_NA };
107
108 #define UPDATE_COVERAGE(i,r) { if(coverage[i] != TEST_RES_FAIL) coverage[i] = r; }
109
110 static void print_test_cov_results(echo_decoder_t *decoder);
111
112 /*************************/
113
114
115 /** init decoder on creation, along with library instance structure */
echo_dcd_init(echo_decoder_t * decoder,ocsd_extern_dcd_inst_t * p_decoder_inst,const echo_dcd_cfg_t * p_config,const ocsd_extern_dcd_cb_fns * p_lib_callbacks)116 void echo_dcd_init(echo_decoder_t *decoder, ocsd_extern_dcd_inst_t *p_decoder_inst, const echo_dcd_cfg_t *p_config, const ocsd_extern_dcd_cb_fns *p_lib_callbacks)
117 {
118 // initialise the decoder instance.
119
120 // zero out the structure
121 memset(decoder, 0, sizeof(echo_decoder_t));
122
123 memcpy(&(decoder->reg_config), p_config, sizeof(echo_dcd_cfg_t)); // copy in the config structure.
124 memcpy(&(decoder->lib_fns), p_lib_callbacks, sizeof(ocsd_extern_dcd_cb_fns)); // copy in the the library callbacks.
125
126 echo_dcd_reset(decoder);
127
128 // fill out the info to pass back to the library.
129
130 // set up the decoder handle, name and CS Trace ID
131 p_decoder_inst->decoder_handle = decoder;
132 p_decoder_inst->p_decoder_name = DECODER_NAME;
133 p_decoder_inst->cs_id = p_config->cs_id;
134
135 // set up the data input callback
136 p_decoder_inst->fn_data_in = echo_dcd_trace_data_in;
137 p_decoder_inst->fn_update_pkt_mon = echo_dcd_update_mon_flags;
138
139 }
140
echo_dcd_pkt_tostr(echo_dcd_pkt_t * pkt,char * buffer,const int buflen)141 void echo_dcd_pkt_tostr(echo_dcd_pkt_t *pkt, char *buffer, const int buflen)
142 {
143 snprintf(buffer, buflen, "ECHOTP{%d} [0x%02X] (0x%08X)", pkt->header & 0x3, pkt->header, pkt->data);
144 }
145
146 /**** Main decoder implementation ****/
echo_dcd_trace_data_in(const void * decoder_handle,const ocsd_datapath_op_t op,const ocsd_trc_index_t index,const uint32_t dataBlockSize,const uint8_t * pDataBlock,uint32_t * numBytesProcessed)147 ocsd_datapath_resp_t echo_dcd_trace_data_in(const void *decoder_handle,
148 const ocsd_datapath_op_t op,
149 const ocsd_trc_index_t index,
150 const uint32_t dataBlockSize,
151 const uint8_t *pDataBlock,
152 uint32_t *numBytesProcessed)
153 {
154 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
155 echo_decoder_t *decoder = (echo_decoder_t *)decoder_handle;
156 UPDATE_COVERAGE(TEST_COV_INDATA,TEST_RES_OK)
157
158 /* Deal with each possible datapath operation.
159 */
160 switch (op)
161 {
162 case OCSD_OP_DATA:
163 resp = echo_dcd_process_data(decoder, index, dataBlockSize, pDataBlock, numBytesProcessed);
164 break;
165
166 case OCSD_OP_EOT:
167 if (decoder->data_in_count > 0)
168 lib_cb_LogError(&(decoder->lib_fns), OCSD_ERR_SEV_WARN, OCSD_ERR_PKT_INTERP_FAIL,decoder->curr_pkt_idx,decoder->reg_config.cs_id,"Incomplete packet at end of trace.\n");
169
170 /* if we are in full decoder mode then generate a generic EOT packet. */
171 if (decoder->createFlags & OCSD_CREATE_FLG_FULL_DECODER)
172 {
173 ocsd_gen_elem_init(&(decoder->out_pkt), OCSD_GEN_TRC_ELEM_EO_TRACE);
174 resp = send_gen_packet(decoder);
175 send_none_data_op(decoder, OCSD_OP_EOT); /* send EOT to any packet monitor in use */
176 }
177 else
178 resp = send_none_data_op(decoder, OCSD_OP_EOT); /*send EOT to packet sink and any packet monitor in use */
179 print_test_cov_results(decoder); /* end of test run - need to print out the coverage data */
180 break;
181
182 case OCSD_OP_FLUSH:
183 /* This decoder never saves a list of incoming packets (which some real decoders may have to according to protocol).
184 Additionally this decoder both processes packets and analyses them so there is no second stage to pass the flush request on to.
185 Therefore there is nothing to flush */
186 break;
187
188 case OCSD_OP_RESET:
189 echo_dcd_reset(decoder);
190 break;
191 }
192 return resp;
193 }
194
echo_dcd_update_mon_flags(const void * decoder_handle,const int flags)195 void echo_dcd_update_mon_flags(const void *decoder_handle, const int flags)
196 {
197 lib_cb_updatePktCBFlags(&((echo_decoder_t *)decoder_handle)->lib_fns, flags);
198 UPDATE_COVERAGE(TEST_COV_INCBFLAGS,TEST_RES_OK)
199 }
200
echo_dcd_reset(echo_decoder_t * decoder)201 void echo_dcd_reset(echo_decoder_t *decoder)
202 {
203 decoder->curr_pkt.header = 0;
204 decoder->data_in_count = 0;
205 decoder->state = DCD_INIT;
206 }
207
echo_dcd_process_data(echo_decoder_t * decoder,const ocsd_trc_index_t index,const uint32_t dataBlockSize,const uint8_t * pDataBlock,uint32_t * numBytesProcessed)208 ocsd_datapath_resp_t echo_dcd_process_data(echo_decoder_t *decoder,
209 const ocsd_trc_index_t index,
210 const uint32_t dataBlockSize,
211 const uint8_t *pDataBlock,
212 uint32_t *numBytesProcessed)
213 {
214 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
215 uint32_t bytesUsed = 0;
216
217 while (OCSD_DATA_RESP_IS_CONT(resp) && (bytesUsed < dataBlockSize))
218 {
219 switch (decoder->state)
220 {
221
222 case DCD_INIT:
223 /* on initialisation / after reset output a not-synced indicator */
224 ocsd_gen_elem_init(&decoder->out_pkt, OCSD_GEN_TRC_ELEM_NO_SYNC);
225 resp = send_gen_packet(decoder);
226 decoder->state = DCD_WAIT_SYNC; /* wait for the first sync point */
227 print_init_test_message(decoder); /* because this is in fact a test decoder - print verification messages */
228 break;
229
230 case DCD_WAIT_SYNC:
231 /* In this 'protocol' sync will be a single 0x00 byte.
232 Some decoders may output "unsynced packets" markers if in packet processing only mode, or on the
233 packet monitor output if in use. We are not bothering here. */
234 if (pDataBlock[bytesUsed] == 0x00)
235 decoder->state = DCD_PROC_PACKETS;
236 bytesUsed++;
237 break;
238
239 case DCD_PROC_PACKETS:
240 /* collect our ECHO_DCD_PKT_SIZE byte packets into the data in buffer */
241 if (decoder->data_in_count < ECHO_DCD_PKT_SIZE)
242 {
243 if (decoder->data_in_count == 0)
244 decoder->curr_pkt_idx = index + bytesUsed; /* record the correct start of packet index in the buffer. */
245 decoder->data_in[decoder->data_in_count++] = pDataBlock[bytesUsed++];
246 }
247
248 /* if we have ECHO_DCD_PKT_SIZE bytes we have a packet */
249 if (decoder->data_in_count == ECHO_DCD_PKT_SIZE)
250 {
251 resp = analyse_packet(decoder);
252 decoder->data_in_count = 0; /* done with the current packet */
253 }
254 break;
255 }
256 }
257 *numBytesProcessed = bytesUsed;
258 return resp;
259 }
260
send_gen_packet(echo_decoder_t * decoder)261 ocsd_datapath_resp_t send_gen_packet(echo_decoder_t *decoder)
262 {
263 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
264 /* Only output generic decode packets if we are in full decode mode. */
265 if (decoder->createFlags & OCSD_CREATE_FLG_FULL_DECODER)
266 {
267 resp = lib_cb_GenElemOp(&decoder->lib_fns, decoder->curr_pkt_idx, decoder->reg_config.cs_id, &decoder->out_pkt);
268 UPDATE_COVERAGE(TEST_COV_GEN_ELEM_CB, (OCSD_DATA_RESP_IS_FATAL(resp) ? TEST_RES_FAIL : TEST_RES_OK))
269 }
270 return resp;
271 }
272
send_none_data_op(echo_decoder_t * decoder,const ocsd_datapath_op_t op)273 ocsd_datapath_resp_t send_none_data_op(echo_decoder_t *decoder, const ocsd_datapath_op_t op)
274 {
275 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
276 ocsd_extern_dcd_cb_fns *p_fns = &(decoder->lib_fns);
277
278 /* send a none data op to the packet monitor or packet sink if in packet processing only mode
279 None data ops have the data parameters all set to 0.
280 */
281
282 /* if the packet monitor callback is in use. */
283 if (lib_cb_usePktMon(p_fns))
284 lib_cb_PktMon(p_fns, op, 0, 0, 0, 0);
285
286 /* if the packet sink is in use then we shouldn't be in full decoder mode.*/
287 if (lib_cb_usePktSink(p_fns))
288 resp = lib_cb_PktDataSink(p_fns, op, 0, 0);
289
290 return resp;
291 }
292
print_init_test_message(echo_decoder_t * decoder)293 void print_init_test_message(echo_decoder_t * decoder)
294 {
295 ocsd_extern_dcd_cb_fns *p_fns = &(decoder->lib_fns);
296 if (lib_cb_LogMsg(p_fns, OCSD_ERR_SEV_ERROR, "Echo_Test_Decoder: Init - LogMsgCB test.\n") == OCSD_OK)
297 UPDATE_COVERAGE(TEST_COV_MSGLOG_CB, TEST_RES_OK)
298 else
299 UPDATE_COVERAGE(TEST_COV_MSGLOG_CB, TEST_RES_FAIL)
300
301 if(lib_cb_LogError(p_fns, OCSD_ERR_SEV_ERROR, OCSD_OK, 0, decoder->reg_config.cs_id, "Echo_Test_Decoder - Init - LogErrorCB test.\n") == OCSD_OK)
302 UPDATE_COVERAGE(TEST_COV_ERRORLOG_CB, TEST_RES_OK)
303 else
304 UPDATE_COVERAGE(TEST_COV_ERRORLOG_CB, TEST_RES_FAIL)
305 }
306
print_test_cov_results(echo_decoder_t * decoder)307 void print_test_cov_results(echo_decoder_t *decoder)
308 {
309 int i;
310 ocsd_extern_dcd_cb_fns *p_fns = &(decoder->lib_fns);
311 static char *results[] = {
312 "Not Tested", "Passed", "Failed"
313 };
314 static char *cov_elem_names[] = {
315 "ERRORLOG_CB",
316 "MSGLOG_CB",
317 "GEN_ELEM_CB",
318 "IDEC_CB",
319 "MEM_ACC_CB",
320 "PKTMON_CB",
321 "PKTSINK_CB",
322 "INDATA",
323 "INCBFLAGS"
324 };
325 char coverage_message[256];
326
327 for (i = 0; i < TEST_COV_END; i++)
328 {
329 sprintf(coverage_message, "Element %s : %s\n",cov_elem_names[i],results[coverage[i]]);
330 if (coverage[TEST_COV_MSGLOG_CB] == TEST_RES_OK) /* check we can use the msg logger for outputting the results */
331 lib_cb_LogMsg(p_fns, OCSD_ERR_SEV_ERROR, coverage_message);
332 else
333 printf("%s", coverage_message);
334 }
335 }
336
337
338 /* This is the packet decode portion of the decoder.
339 * incoming protocol packets are analysed to create generic output packets.
340 */
analyse_packet(echo_decoder_t * decoder)341 ocsd_datapath_resp_t analyse_packet(echo_decoder_t * decoder)
342 {
343 ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
344 ocsd_extern_dcd_cb_fns *p_fns = &(decoder->lib_fns);
345 uint32_t num_mem_bytes = 4;
346 uint8_t mem_buffer[4];
347 ocsd_instr_info instr_info;
348 ocsd_err_t err;
349
350 /* create a packet from the data */
351 decoder->curr_pkt.header = decoder->data_in[0];
352 decoder->curr_pkt.data = *((uint32_t *)&decoder->data_in[1]);
353
354 /* if the packet monitor callback is in use - output the newly created packet. */
355 if (lib_cb_usePktMon(p_fns))
356 {
357 lib_cb_PktMon(p_fns, OCSD_OP_DATA, decoder->curr_pkt_idx, (const void *)(&decoder->curr_pkt), ECHO_DCD_PKT_SIZE, decoder->data_in);
358 UPDATE_COVERAGE(TEST_COV_PKTMON_CB, TEST_RES_OK)
359 }
360
361 /* if the packet sink is in use then we shouldn't be in full decoder mode.*/
362 if (lib_cb_usePktSink(p_fns))
363 {
364 resp = lib_cb_PktDataSink(p_fns, OCSD_OP_DATA, decoder->curr_pkt_idx, (const void *)(&decoder->curr_pkt));
365 UPDATE_COVERAGE(TEST_COV_PKTSINK_CB, (OCSD_DATA_RESP_IS_FATAL(resp) ? TEST_RES_FAIL : TEST_RES_OK))
366 }
367 else if (decoder->createFlags & OCSD_CREATE_FLG_FULL_DECODER) /* no packet sink so are we full decoder? */
368 {
369 /* Full decode - generate generic output packets.
370
371 A real decoder will sometimes require multiple input packets per output packet, or may generate multiple output
372 packets per single input packet. Here we stick at 1:1 for test simplicity.
373
374 This code will also test the infrastructure callbacks to ensure that everything holds together correctly.
375 */
376
377 /* nominally 4 types of packet */
378 switch (decoder->curr_pkt.header & 0x3)
379 {
380 case 0:
381 ocsd_gen_elem_init(&decoder->out_pkt, OCSD_GEN_TRC_ELEM_CUSTOM); /* full custom packet */
382 decoder->out_pkt.extended_data = 1;/* mark the extended ptr in use */
383 decoder->out_pkt.ptr_extended_data = decoder->data_in; /* the custom packet data in this protocol just the packet itself (hence 'echo')*/
384 break;
385
386 case 1:
387 /* custom decoders can re-use existing packet types if they follow the rules for those types. */
388 ocsd_gen_elem_init(&decoder->out_pkt, OCSD_GEN_TRC_ELEM_INSTR_RANGE);
389 /* fake up an address range using the input data */
390 decoder->out_pkt.st_addr = decoder->curr_pkt.data & 0xFFFFFFF0;
391 decoder->out_pkt.en_addr = decoder->curr_pkt.data + 0x10 + (((uint32_t)decoder->curr_pkt.header) << 2);
392 decoder->out_pkt.isa = ocsd_isa_custom;
393 decoder->out_pkt.last_instr_exec = (decoder->curr_pkt.header & 0x4) ? 1 : 0;
394 break;
395
396 case 2:
397 /* test the memory access callback. */
398 err = lib_cb_MemAccess(p_fns, decoder->curr_pkt.data & 0xFFFFFFF0, decoder->reg_config.cs_id, OCSD_MEM_SPACE_ANY, &num_mem_bytes, mem_buffer);
399 if (err != OCSD_OK)
400 lib_cb_LogError(p_fns, OCSD_ERR_SEV_ERROR, err, decoder->curr_pkt_idx, decoder->reg_config.cs_id, "Error accessing memory area\n.");
401 UPDATE_COVERAGE(TEST_COV_MEM_ACC_CB,(err == OCSD_OK ? TEST_RES_OK : TEST_RES_FAIL))
402 if (num_mem_bytes == 0)
403 {
404 /* unable to read the address... */
405 ocsd_gen_elem_init(&decoder->out_pkt, OCSD_GEN_TRC_ELEM_ADDR_NACC);
406 decoder->out_pkt.st_addr = decoder->curr_pkt.data & 0xFFFFFFF0;
407 }
408 else
409 {
410 /* try something different */
411 ocsd_gen_elem_init(&decoder->out_pkt, OCSD_GEN_TRC_ELEM_CYCLE_COUNT);
412 decoder->out_pkt.cycle_count = *((uint32_t *)mem_buffer);
413 decoder->out_pkt.has_cc = 1;
414 }
415 break;
416
417 case 3:
418 /* test the ARM instruction decode callback */
419 instr_info.pe_type.arch = ARCH_V8;
420 instr_info.pe_type.profile = profile_CortexA;
421 instr_info.isa = ocsd_isa_aarch64;
422 instr_info.opcode = decoder->curr_pkt.data;
423 instr_info.instr_addr = decoder->curr_pkt.data & 0xFFFFF000;
424 instr_info.dsb_dmb_waypoints = 0;
425
426 err = lib_cb_DecodeArmInst(p_fns, &instr_info);
427 UPDATE_COVERAGE(TEST_COV_IDEC_CB, (err == OCSD_OK ? TEST_RES_OK : TEST_RES_FAIL))
428 if (err != OCSD_OK)
429 {
430 lib_cb_LogError(p_fns, OCSD_ERR_SEV_ERROR, err, decoder->curr_pkt_idx, decoder->reg_config.cs_id, "Error decoding instruction\n.");
431 ocsd_gen_elem_init(&decoder->out_pkt, OCSD_GEN_TRC_ELEM_CUSTOM);
432 decoder->out_pkt.has_ts = 1;
433 decoder->out_pkt.timestamp = decoder->curr_pkt.data;
434 }
435 else
436 {
437 ocsd_gen_elem_init(&decoder->out_pkt, OCSD_GEN_TRC_ELEM_INSTR_RANGE);
438 /* fake up an address range using the input data */
439 decoder->out_pkt.st_addr = decoder->curr_pkt.data & 0xFFFFFFF0;
440 decoder->out_pkt.en_addr = decoder->curr_pkt.data + 0x10 + (((uint32_t)decoder->curr_pkt.header) << 2);
441 decoder->out_pkt.isa = ocsd_isa_aarch64;
442 decoder->out_pkt.last_instr_exec = (decoder->curr_pkt.header & 0x4) ? 1 : 0;
443 decoder->out_pkt.last_i_type = instr_info.type;
444 decoder->out_pkt.last_i_subtype = instr_info.sub_type;
445 }
446 break;
447 }
448 resp = send_gen_packet(decoder);
449 }
450 return resp;
451 }
452