orca-sim
RspServer.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  * This file is part of project ORCA. More information on the project
3  * can be found at the following repositories at GitHub's website.
4  *
5  * http://https://github.com/andersondomingues/orca-sim
6  * http://https://github.com/andersondomingues/orca-software
7  * http://https://github.com/andersondomingues/orca-mpsoc
8  * http://https://github.com/andersondomingues/orca-tools
9  *
10  * Copyright (C) 2018-2020 Anderson Domingues, <ti.andersondomingues@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 ******************************************************************************/
26 #include <sstream>
27 
28 #include "RspServer.hpp"
29 #include "DataConvertionHelper.hpp"
30 
31 #define RSP_DEBUG 1
32 
36 
37 template <typename T>
39  std::string ipaddr, uint32_t udpport) {
40 
41  _udpport = udpport;
42  _ipaddr = ipaddr;
43 
44  // store external state reference and memory
45  _state = state;
46  _memory = mem;
47 
48  // create udp server
49  _server = new UdpAsyncServer(udpport);
50  std::cout << "[" << udpport << "]";
51 
52  _bp_list = new std::list<T>();
53 }
54 
55 // remove udp server instance
56 template <typename T>
58  delete _server;
59 }
60 
61 // checksum
62 template <typename T>
63 uint8_t RspServer<T>::Checksum(char* buffer, int length) {
64  uint8_t checksum = 0;
65 
66  while (length--)
67  checksum += *buffer++;
68 
69  return checksum;
70 }
71 
72 template <typename T>
73 int RspServer<T>::Respond(std::string data) {
74  // packet response should be "$data#checksum"
75  // copy the $ charater to the output
76  _output_buffer[0] = '$';
77 
78  // copy data into output buffer ( -1 eliminates terminating character)
79  memcpy(&_output_buffer[1], data.c_str(), data.length());
80 
81  // add separator to the ourput
82  _output_buffer[data.length() + 1] = '#';
83 
84  // calculate checksum and append it to the pkt
85  uint8_t checksum = this->Checksum(&_output_buffer[1], data.length());
86  snprintf(reinterpret_cast<char*>(&_output_buffer[data.length() + 2]),
87  sizeof(uint32_t), "%02x", checksum);
88 
89  // add terminating charaters to the string
90  _output_buffer[data.length() + 4] = '\0';
91 
92  #ifdef RSP_DEBUG
93  std::cout << "\033[0;36m<--\033[0m" << _output_buffer << std::endl;
94  #endif
95 
96  // send via udp (length = '$' + data + '#' + 'XX')
97  return _server->Send(_output_buffer, data.length() + 4);
98 }
99 
100 template <typename T>
102  #ifdef RSP_DEBUG
103  std::cout << "\033[0;36m<-- ack\033[0m" << std::endl;
104  #endif
105 
106  _output_buffer[0] = '+';
107  return _server->Send(_output_buffer, 1);
108 }
109 
110 template <typename T>
112  _output_buffer[0] = '-';
113  return _server->Send(_output_buffer, 1);
114 }
115 
116 template <typename T>
118  // check whether we reach a breakpoint
119  if (_state->bp == 0) {
120  for (typename std::list<T>::iterator i = _bp_list->begin();
121  i != _bp_list->end(); ++i) {
122  if (_state->pc == *i) {
123  // breakpoint hit
124  // this->Respond("T05swbreak:");
125  this->Respond("S05");
126  _state->bp = 1;
127  break;
128  }
129  }
130  }
131 
132  // if CPU reached a breakpoint
133  if (_state->bp == 1) {
134  // _state->bp = 0; // clear breakpoint
135  _state->pause = 1; // pause cpu
136  _state->steps = 0; // reset steps counter (bp has priority over s)
137  // this->Respond("T05:");
138 
139  // if CPU no pause with no bp
140  } else if (_state->pause == 0) {
141  if (_state->steps > 0) {
142  _state->steps -= 1;
143 
144  // limit of steps reach, pausing
145  if (_state->steps == 0) {
146  _state->pause = 1;
147  this->Respond("S05"); // 05 = TRAP
148  }
149  }
150  }
151 
152  return 1;
153 }
154 
155 template <typename T>
157  // check whether the udp server could receive another packet
158  int recv_bytes = _server->Receive(_input_buffer);
159 
160  // check whether some packet have been received
161  if (recv_bytes > 0) {
162  // add termination to received string, garantees printable output
163  _input_buffer[recv_bytes] = '\0';
164 
165  // received packet is an ACK, ignoring (if connection is reliable)
166  if (_input_buffer[0] == '+') {
167  #ifdef RSP_DEBUG
168  std::cout << "\033[0;31m-->\033[0m" << _input_buffer << std::endl;
169  #endif
170 
171  // retransmission will not be required, ignoring any request
172  } else if (_input_buffer[0] == '-') {
173  #ifdef RSP_DEBUG
174  std::cout << "\033[0;31m-->\033[0m" << _input_buffer << std::endl;
175  #endif
176 
177  // check whether the packet is valid
178  // TODO(ad): parse checksum
179  } else if (_input_buffer[0] == '$') {
180  #ifdef RSP_DEBUG
181  std::cout << "\033[0;36m-->\033[0m" << _input_buffer << std::endl;
182  #endif
183 
184  // acknowledge all valid packets
185  this->Ack();
186 
187  // message handler depends on the first character
188  switch (_input_buffer[1]) {
189  case 'q': return this->Handle_q(_input_buffer); // query pckt
190  case 'g': return this->Handle_g(_input_buffer); // get regs
191  case '?': return this->Handle_Question(_input_buffer); // hlt
192  case 'c': return this->Handle_c(_input_buffer); // continue
193  case 'C': return this->Handle_C(_input_buffer); // continue
194  case 's': return this->Handle_s(_input_buffer); // continue
195  case 'H': return this->Handle_H(_input_buffer); // Hc
196  case 'm': return this->Handle_m(_input_buffer); // mem read
197  // case 'M': return this->Handle_M(_input_buffer); // mem write
198  case 'p': return this->Handle_p(_input_buffer); // reg read
199  case 'P': return this->Handle_P(_input_buffer); // reg write
200  case 'v': return this->Handle_v(_input_buffer); // vCont
201  case 'X': return this->Handle_X(_input_buffer); // bin mem wr
202  case 'z': return this->Handle_z(_input_buffer); // set bp
203  case 'Z': return this->Handle_Z(_input_buffer); // clear bp
204  // case 'Q': return this->Handle_Q(_input_buffer);
205 
206  // commands not implemented by the server
207  // must responded with the empty response "$#00".
208  default:
209 
210  #ifdef RSP_DEBUG
211  std::cout << "\033[0;31mwnr: unknown packet type: \033[0m"
212  << _input_buffer << std::endl;
213  #endif
214 
215  // return this->Respond(RSP_EMPTY_RESPONSE);
216  return this->Respond("");
217  }
218  } else {
219  #ifdef RSP_DEBUG
220  std::cout
221  << "\033[0;31mwrn: dropped packet with unknown prefix:\033[0m"
222  << _input_buffer << std::endl;
223  #endif
224  }
225  }
226 
227  return -1; // TODO(ad): enum for statuses (could not recv a pkt)
228 }
229 
230 template <typename T>
231 int RspServer<T>::Handle_X(char* buffer) {
232  if (memcmp(buffer, "$X", 2) == 0) {
233  // locate addr to start writing to
234  int addr = strhti(&buffer[2], 10);
235 
236  // find number of bytes to write
237  int i = strfind(buffer, ',', 14);
238  int size = strhti(&buffer[i+1], 10);
239 
240  // respond ok if test message
241  if (size == 0) {
242  return this->Respond("OK");
243 
244  // otherwise, write to memory
245  } else {
246  // find the beggining of the stream
247  i = strfind(buffer, ':', 20);
248  uint8_t* raw = reinterpret_cast<uint8_t*>(&buffer[i+1]);
249 
250  // note: binary data comes from GDB
251  // client with bytes in the correct
252  // order, so no endianess treatment is
253  // necessary.
254 
255  // remove escape chars (0x7d = '}')
256  for (int j = 0, i = 0; i < size; i++) {
257  if (raw[j] == 0x7d) {
258  raw[i] = (uint8_t)(raw[++j] ^ 0x20);
259  j++;
260  } else {
261  raw[i] = raw[j++];
262  }
263  }
264 
265  // write the whole chunk to the memory of the device
266  _memory->Write(addr, reinterpret_cast<MemoryType*>(raw), size);
267  return this->Respond("OK");
268  }
269  } else {
270  std::cout << "unhandled packet 'X'" << std::endl;
271  }
272 
273  return -1;
274 }
275 
276 template <typename T>
277 int RspServer<T>::Handle_g(char* buffer) {
278  // there is only one 'g' message, just report registers
279  if (memcmp(buffer, "$g", 2) == 0) {
280  // prints as [0x]00000000,
281  // (number of regs + pc) * size of reg * 2 char per byte + '\0'
282  char reg_data[(NUMBER_OF_REGISTERS + 1) * sizeof(T) * 2 + 1];
283 
284  // convert whole array to hexstring
285  hexstr(reinterpret_cast<char*>(reg_data),
286  reinterpret_cast<char*>(_state->regs), NUMBER_OF_REGISTERS + 1);
287 
288  // do not add trailing character as hexstr adds it
289  return this->Respond(std::string(reg_data));
290 
291  } else {
292  std::cout << "unhandled packet 'g'" << std::endl;
293  }
294 
295  return -1;
296 }
297 
298 template <typename T>
299 int RspServer<T>::Handle_v(char* buffer) {
300  // vCont, must reply empty (not supported)
301  if (memcmp(buffer, "$vCont", 5) == 0) {
302  return this->Respond(RSP_EMPTY_RESPONSE);
303 
304  // vKill, gdb ask to stop debugging
305  // TODO(ad): set state to "not running"
306  } else if (memcmp(buffer, "$vKill", 5) == 0) {
307  this->Respond("OK");
308  exit(0); // TODO(ad): verify whether this is necessary
309 
310  // vMustReplyEmpty, must reply empty
311  } else if (memcmp(buffer, "$vMustReplyEmpty", 16) == 0) {
312  return this->Respond(RSP_EMPTY_RESPONSE);
313 
314  // unhandled messages
315  } else {
316  std::cout << "unhandled packet 'v'" << std::endl;
317  }
318 
319  return 1;
320 }
321 
322 // query packets (upper case Q is for SET)
323 template <typename T>
324 int RspServer<T>::Handle_Q(char* buffer) {
325  // QStartNoAckMode, disables aknowledgement messages (+)
326  if (memcmp(buffer, "$QStartNoAckMode", 15) == 0) {
327  this->Respond("OK");
328  } else {
329  std::cout << "unhandled packet 'Q'" << std::endl;
330  }
331 
332  return 0;
333 }
334 
335 // query packets (lower case Q is for GET)
336 template <typename T>
337 int RspServer<T>::Handle_q(char* buffer) {
338  // qC is not supported
339  if (memcmp(buffer, "$qC", 3) == 0) {
340  return this->Respond("QC1");
341 
342  // is always attached
343  } else if (memcmp(buffer, "$qAttached", 7) == 0) {
344  return this->Respond("0");
345  // return this->Respond("");
346 
347  // target doesn't move the offsets, reply all zero
348  } else if (memcmp(buffer, "$qOffsets", 6) == 0) {
349  return this->Respond("Text=0;Data=0;Bss=0");
350 
351  // notify target that client can provide symbols
352  } else if (memcmp(buffer, "$qSymbol", 6) == 0) {
353  return this->Respond("OK");
354 
355  // report supported packet size and request to diable ack mode
356  } else if (memcmp(buffer, "$qSupported", 11) == 0) {
357  return this->Respond("PacketSize=FFF"); // QStartNoAckMode+
358 
359  // currently running threads (one only)
360  } else if (memcmp(buffer, "$qfThreadInfo", 11) == 0) {
361  // return this->Respond("m 0"); //alternatively "m1"
362  return this->Respond("m1"); // alternatively "m1"
363 
364  // report the end of list (see qfThreadInfo)
365  } else if (memcmp(buffer, "$qsThreadInfo", 11) == 0) {
366  return this->Respond("l");
367 
368  // report thread status
369  } else if (memcmp(buffer, "$qTStatus", 8) == 0) {
370  return this->Respond("T0");
371 
372  // qTfv and qTsv
373  } else {
374  std::cout << "unhandled qT packet, sent empty response" << std::endl;
375  return this->Respond("");
376  }
377 
378  return -1;
379 }
380 
381 // why did the program stop?
382 template <typename T>
384  return this->Respond("S00"); // <- exited ok
385 }
386 
387 // continue at the current address
388 template <typename T>
389 int RspServer<T>::Handle_C(char* buffer) {
390  if (memcmp(buffer, "$C", 2) == 0) {
391  // disable pause mode and continue
392  // in the same address as it is
393  _state->pause = 0x0;
394  return this->Respond("OK");
395  } else {
396  std::cout << "unhandled 'c' packet, sent empty response" << std::endl;
397  return this->Respond("");
398  }
399 
400  return -1;
401 }
402 
403 // continue at the current address
404 template <typename T>
405 int RspServer<T>::Handle_c(char* buffer) {
406  // @TODO: implement "continue at addr"
407  if (memcmp(buffer, "$c", 2) == 0) {
408  // disable pause mode and continue
409  // in the same address as it is
410  _state->pause = 0x0;
411  return this->Respond("OK");
412  } else {
413  std::cout << "unhandled 'c' packet, sent empty response" << std::endl;
414  return this->Respond("");
415  }
416 
417  return -1;
418 }
419 
420 // step X instructions
421 template <typename T>
422 int RspServer<T>::Handle_s(char* buffer) {
423  if (memcmp(buffer, "$s", 2) == 0) {
424  // single step
425  if (buffer[2] == '#') {
426  _state->steps = 1; // set to stop cpu in next cycle
427  _state->pause = 0x0; // remove pause until then
428  this->Respond("OK");
429  } else {
430  std::cout << "multiple step not implemented, sent empty response"
431  << std::endl;
432  this->Respond("");
433  }
434  } else {
435  std::cout << "unhandled 's' packet, sent empty response" << std::endl;
436  this->Respond("");
437  }
438  return 0;
439 }
440 
441 // select thread for subsequent actions, thread-0
442 // is always selected
443 template <typename T>
445  return this->Respond("OK");
446 }
447 
448 template <typename T>
450  return 0;
451 }
452 
453 // 'm': read value from the main memory
454 // format => "$mX,Y#CC", where X is the
455 // address, Y is the size
456 template <typename T>
457 int RspServer<T>::Handle_m(char* buffer) {
458  // only one packet type 'm'
459  if (memcmp(buffer, "$m", 2) == 0) {
460  uint32_t addr = 0, size = 0;
461  int end = 0, comma = 0;
462 
463  // can crash here if no "#" in the message
464  // TODO(ad): make sure that at least one "#" is present
465  end = strfind(buffer, '#', 100);
466 
467  // raddr starts after the 'm' character and
468  // goes until the ','
469  addr = strhti(&buffer[2], end - 2);
470 
471  // find the ',' char
472  comma = strfind(buffer, ',', end);
473 
474  // find size. NOTE: size means "X 16-bit words"
475  size = strhti(&buffer[comma+1], end); // * 2;
476 
477  // @TODO: why does GBD ask for addr=0?
478  // @TODO: remove restriction on 8bit memory model (another T param?)
479  if (addr < _memory->GetBase() || addr > _memory->GetLastAddr()) {
480  std::cout << "gdbcli requested data from outside memory space:"
481  << std::endl;
482  return this->Respond(""); // return 8 bits char
483  }
484 
485  // read data from memory, 0x00 for each byte
486  MemoryType data[size * sizeof(MemoryType) * 2];
487  _memory->Read(addr, data, size);
488 
489  // store hexstring, "00000000" per integer, "00" per byte + '\0'
490  char str_data[size * 2 + 8]; // add 8 for integer-alignment
491 
492  // convert from byte to hexstring
493  hexstr(reinterpret_cast<char*>(str_data),
494  reinterpret_cast<char*>(data), size / 2);
495 
496  return this->Respond(std::string(reinterpret_cast<char*>(str_data)));
497  } else {
498  std::cout << "unhandled 'm' packet, sent empty response" << std::endl;
499  return this->Respond("");
500  }
501  return -1;
502 }
503 
504 template <typename T>
506  return 0;
507 }
508 
509 // read registers
510 template <typename T>
511 int RspServer<T>::Handle_p(char* buffer) {
512  // only one 'p' type packet
513  if (memcmp(buffer, "$p", 2) == 0) {
514  // parse register id
515  char digit[4] = "\0\0\0";
516 
517  // get the two digits with hexa-code reg id
518  if (buffer[2] >= 48 && buffer[2] <= 57) digit[0] = buffer[2];
519  if (buffer[3] >= 48 && buffer[3] <= 57) digit[1] = buffer[3];
520 
521  // convert ascii hexa to integer, so we can access the array
522  unsigned int reg = static_cast<int>(strtol(digit, NULL, 16));
523 
524  // allocate space for the ascii hexa containing the value
525  // we add 1 because sprintf adds \0 to the end
526  char reg_data[sizeof(T) * 2 + 1];
527 
528  // print the value as hexa string into the allocated space
529  snprintf(reg_data, sizeof(reg_data), "%0X",
530  (unsigned int)_state->regs[reg]);
531 
532  this->Respond(std::string(reg_data));
533 
534  } else {
535  std::cout << "unhandled 'p' packet, sent empty response" << std::endl;
536  return this->Respond("");
537  }
538  return -1;
539 }
540 
541 // write register
542 template <typename T>
543 int RspServer<T>::Handle_P(char* buffer) {
544  if (memcmp(buffer, "$P", 2) == 0) {
545  // locate register address
546  int addr = strhti(&buffer[2], 3);
547 
548  // locate the '=' symbol
549  int eqsymb = strfind(buffer, '=', 10);
550 
551  // get the register value
552  int value = strhti(&buffer[eqsymb+1], 10);
553  value = endswap(value);
554 
555  // set register
556  _state->regs[addr] = value;
557  return this->Respond("OK");
558 
559  } else {
560  std::cout << "unhandled 'P' packet, sent empty response" << std::endl;
561  return this->Respond("");
562  }
563 
564  return -1;
565 }
566 
567 
568 // adds a break point, only software supported
569 template <typename T>
570 int RspServer<T>::Handle_Z(char* buffer) {
571  if (memcmp(buffer, "$Z", 2) == 0) {
572  // locate the comma char
573  int comma = strfind(buffer, ',', 10);
574 
575  // parse address
576  T addr = strhti(&buffer[comma + 1], 10);
577 
578  // check whether the address is in the list already
579  bool has_addr_already = false;
580 
581  for (typename std::list<T>::iterator i = _bp_list->begin();
582  i != _bp_list->end(); ++i) {
583  if (addr == *i) {
584  std::cout
585  << "warn: there's a breakpoint in this address already"
586  << std::endl;
587  has_addr_already = true;
588  break;
589  }
590  }
591 
592  // add address to the list
593  if (!has_addr_already) _bp_list->push_back(addr);
594 
595  // !!ignoring breakpoint size, not supported by arch
596  // !!treating all bp as software bp, hw bp not supported
597  return this->Respond("OK");
598 
599  } else {
600  std::cout << "unhandled 'Z' packet, sent empty response" << std::endl;
601  return this->Respond("");
602  }
603 
604  return -1;
605 }
606 
607 // removes a breakpoint, only software supported
608 template <typename T>
609 int RspServer<T>::Handle_z(char* buffer) {
610  if (memcmp(buffer, "$z", 2) == 0) {
611  // locate the comma char
612  int comma = strfind(buffer, ',', 10);
613 
614  // parse address
615  T addr = strhti(&buffer[comma + 1], 10);
616 
617  // remove the address from the list (if there)
618  _bp_list->remove(addr);
619 
620  // !!ignoring breakpoint size, not supported by arch
621  // !!treating all bp as software bp, hw bp not supported
622  return this->Respond("OK");
623 
624  } else {
625  std::cout << "unhandled 'z' packet, sent empty response" << std::endl;
626  return this->Respond("");
627  }
628 
629  return 0;
630 }
This class implements an asynchonous udp server.
int strfind(char *buffer, char find, int limit)
String find.
int strhti(char *buffer, int length)
String-to-int.
#define RSP_EMPTY_RESPONSE
Definition: RspServer.hpp:43
This class models a memory module.
Definition: Memory.hpp:55
uint32_t endswap(uint32_t value)
Endianess Swap.
#define NUMBER_OF_REGISTERS
Defines a generic state model for use within processor models.
#define MemoryType
Definition: MemoryType.hpp:33
void hexstr(char *output, char *input, uint32_t integers)
String to hex.