1 : /*
2 : * Copyright 2010 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can
4 : * be found in the LICENSE file.
5 : */
6 :
7 : #include <assert.h>
8 : #include <string.h>
9 : #include <stdlib.h>
10 :
11 : #include <string>
12 : #include <sstream>
13 :
14 : #include "native_client/src/trusted/gdb_rsp/packet.h"
15 : #include "native_client/src/trusted/gdb_rsp/session.h"
16 : #include "native_client/src/trusted/gdb_rsp/util.h"
17 :
18 : #include "native_client/src/trusted/port/mutex.h"
19 : #include "native_client/src/trusted/port/platform.h"
20 : #include "native_client/src/trusted/port/transport.h"
21 :
22 : using port::IPlatform;
23 : using port::ITransport;
24 : using port::IMutex;
25 : using port::MutexLock;
26 :
27 : // Use a timeout of 1 second
28 : int const kSessionTimeoutMs = 1000;
29 :
30 : namespace gdb_rsp {
31 :
32 6 : Session::Session()
33 : : mutex_(NULL),
34 : io_(NULL),
35 : flags_(0),
36 : seq_(0),
37 6 : connected_(false) {
38 6 : }
39 :
40 6 : Session::~Session() {
41 6 : if (mutex_) IMutex::Free(mutex_);
42 6 : }
43 :
44 :
45 7 : bool Session::Init(port::ITransport *transport) {
46 7 : if (NULL == transport) return false;
47 :
48 6 : mutex_ = IMutex::Allocate();
49 6 : if (NULL == mutex_) return false;
50 :
51 6 : connected_ = true;
52 6 : io_ = transport;
53 6 : return true;
54 : }
55 :
56 3 : void Session::SetFlags(uint32_t flags) {
57 3 : flags_ |= flags;
58 3 : }
59 :
60 2 : void Session::ClearFlags(uint32_t flags) {
61 2 : flags_ &= ~flags;
62 2 : }
63 :
64 72 : uint32_t Session::GetFlags() {
65 72 : return flags_;
66 : }
67 :
68 17 : bool Session::DataAvailable() {
69 17 : assert(io_);
70 :
71 17 : return io_->ReadWaitWithTimeout(kSessionTimeoutMs);
72 : }
73 :
74 13 : bool Session::Connected() {
75 13 : return connected_;
76 : }
77 :
78 17 : bool Session::GetChar(char *ch) {
79 17 : assert(io_);
80 :
81 : // Attempt to select this IO for reading.
82 17 : if (DataAvailable() == false) return false;
83 :
84 17 : int32_t len = io_->Read(ch, 1);
85 :
86 : // If data is "availible" but we can't read, it must be closed.
87 17 : if (len < 1) {
88 2 : io_->Disconnect();
89 2 : connected_ = false;
90 2 : return false;
91 : }
92 :
93 15 : return true;
94 : }
95 :
96 :
97 20 : bool Session::SendPacket(Packet *pkt) {
98 20 : MutexLock lock(mutex_);
99 : char ch;
100 :
101 1 : do {
102 20 : if (!SendPacketOnly(pkt)) return false;
103 :
104 : // If ACKs are off, we are done.
105 20 : if (GetFlags() & IGNORE_ACK) break;
106 :
107 : // Otherwise, poll for '+'
108 1 : if (!GetChar(&ch)) return false;
109 :
110 : // Retry if we didn't get a '+'
111 : } while (ch != '+');
112 :
113 20 : return true;
114 : }
115 :
116 :
117 2 : bool Session::SendPacketOnly(Packet *pkt) {
118 2 : MutexLock lock(mutex_);
119 :
120 : const char *ptr;
121 : char ch;
122 2 : std::stringstream outstr;
123 :
124 2 : char run_xsum = 0;
125 : int32_t seq;
126 :
127 2 : ptr = pkt->GetPayload();
128 :
129 2 : if (!pkt->GetSequence(&seq) && (GetFlags() & USE_SEQ)) {
130 0 : pkt->SetSequence(seq_++);
131 : }
132 :
133 : // Signal start of response
134 2 : outstr << '$';
135 :
136 : // If there is a sequence, send as two nibble 8bit value + ':'
137 2 : if (pkt->GetSequence(&seq)) {
138 0 : IntToNibble((seq & 0xFF) >> 4, &ch);
139 0 : outstr << ch;
140 0 : run_xsum += ch;
141 :
142 0 : IntToNibble(seq & 0xF, &ch);
143 0 : outstr << ch;
144 0 : run_xsum += ch;
145 :
146 0 : ch = ':';
147 0 : outstr << ch;
148 0 : run_xsum += ch;
149 : }
150 :
151 : // Send the main payload
152 2 : int offs = 0;
153 12 : while ((ch = ptr[offs++]) != 0) {
154 8 : outstr << ch;
155 8 : run_xsum += ch;
156 : }
157 :
158 : // Send XSUM as two nible 8bit value preceeded by '#'
159 2 : outstr << '#';
160 2 : IntToNibble((run_xsum >> 4) & 0xF, &ch);
161 2 : outstr << ch;
162 2 : IntToNibble(run_xsum & 0xF, &ch);
163 2 : outstr << ch;
164 :
165 2 : return SendStream(outstr.str().data());
166 : }
167 :
168 : // We do not take the mutex here since we already have it
169 : // this function is protected so it can't be called directly.
170 4 : bool Session::SendStream(const char *out) {
171 4 : int32_t len = static_cast<int32_t>(strlen(out));
172 4 : int32_t sent = 0;
173 :
174 4 : assert(io_);
175 :
176 12 : while (sent < len) {
177 4 : const char *cur = &out[sent];
178 4 : int32_t tx = io_->Write(cur, len - sent);
179 :
180 4 : if (tx <= 0) {
181 0 : IPlatform::LogWarning("Send of %d bytes : '%s' failed.\n", len, out);
182 0 : io_->Disconnect();
183 0 : connected_ = false;
184 0 : return false;
185 : }
186 :
187 4 : sent += tx;
188 : }
189 :
190 4 : if (GetFlags() & DEBUG_SEND) IPlatform::LogInfo("TX %s\n", out);
191 4 : return true;
192 : }
193 :
194 :
195 : // Attempt to receive a packet
196 4 : bool Session::GetPacket(Packet *pkt) {
197 4 : assert(io_);
198 :
199 4 : MutexLock lock(mutex_);
200 :
201 : char run_xsum, fin_xsum, ch;
202 4 : std::string in;
203 : int has_seq, offs;
204 :
205 : // If nothing is waiting, return false
206 4 : if (!io_->ReadWaitWithTimeout(kSessionTimeoutMs)) return false;
207 :
208 : // Toss characters until we see a start of command
209 2 : do {
210 4 : if (!GetChar(&ch)) return false;
211 2 : in += ch;
212 : } while (ch != '$');
213 :
214 2 : retry:
215 2 : has_seq = 1;
216 2 : offs = 0;
217 :
218 : // If nothing is waiting, return NONE
219 2 : if (!io_->ReadWaitWithTimeout(kSessionTimeoutMs)) return false;
220 :
221 : // Clear the stream
222 2 : pkt->Clear();
223 :
224 : // Prepare XSUM calc
225 2 : run_xsum = 0;
226 2 : fin_xsum = 0;
227 :
228 : // Stream in the characters
229 6 : while (1) {
230 8 : if (!GetChar(&ch)) return false;
231 :
232 8 : in += ch;
233 : // Check SEQ statemachine xx:
234 8 : switch (offs) {
235 : case 0:
236 : case 1:
237 4 : if (!NibbleToInt(ch, 0)) has_seq = 0;
238 4 : break;
239 :
240 : case 2:
241 2 : if (ch != ':') has_seq = 0;
242 : break;
243 : }
244 8 : offs++;
245 :
246 : // If we see a '#' we must be done with the data
247 8 : if (ch == '#') break;
248 :
249 : // If we see a '$' we must have missed the last cmd
250 6 : if (ch == '$') {
251 0 : IPlatform::LogInfo("RX Missing $, retry.\n");
252 0 : goto retry;
253 : }
254 : // Keep a running XSUM
255 6 : run_xsum += ch;
256 6 : pkt->AddRawChar(ch);
257 : }
258 :
259 :
260 : // Get two Nibble XSUM
261 2 : if (!GetChar(&ch)) return false;
262 2 : in += ch;
263 :
264 : int val;
265 2 : NibbleToInt(ch, & val);
266 2 : fin_xsum = val << 4;
267 :
268 2 : if (!GetChar(&ch)) return false;
269 2 : in += ch;
270 2 : NibbleToInt(ch, &val);
271 2 : fin_xsum |= val;
272 :
273 2 : if (GetFlags() & DEBUG_RECV) IPlatform::LogInfo("RX %s\n", in.data());
274 :
275 : // Pull off teh sequence number if we have one
276 2 : if (has_seq) {
277 : uint8_t seq;
278 : char ch;
279 :
280 0 : pkt->GetWord8(&seq);
281 0 : pkt->SetSequence(seq);
282 0 : pkt->GetRawChar(&ch);
283 0 : if (ch != ':') {
284 0 : IPlatform::LogError("RX mismatched SEQ.\n");
285 0 : return false;
286 : }
287 : }
288 :
289 : // If ACKs are off, we are done.
290 2 : if (GetFlags() & IGNORE_ACK) return true;
291 :
292 : // If the XSUMs don't match, signal bad packet
293 2 : if (fin_xsum == run_xsum) {
294 2 : char out[4] = { '+', 0, 0, 0};
295 : int32_t seq;
296 :
297 : // If we have a sequence number
298 2 : if (pkt->GetSequence(&seq)) {
299 : // Respond with Sequence number
300 0 : IntToNibble(seq >> 4, &out[1]);
301 0 : IntToNibble(seq & 0xF, &out[2]);
302 : }
303 2 : return SendStream(out);
304 : } else {
305 : // Resend a bad XSUM and look for retransmit
306 0 : SendStream("-");
307 :
308 0 : IPlatform::LogInfo("RX Bad XSUM, retry\n");
309 0 : goto retry;
310 : }
311 :
312 0 : return true;
313 : }
314 :
315 : } // End of namespace gdb_rsp
316 :
|