1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#define round_down(a, b) \
30    ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
31
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <errno.h>
37
38#include <algorithm>
39#include <vector>
40
41#include <android-base/file.h>
42#include <android-base/stringprintf.h>
43#include <android-base/unique_fd.h>
44#include <sparse/sparse.h>
45#include <utils/FileMap.h>
46
47#include "fastboot.h"
48#include "transport.h"
49
50static std::string g_error;
51
52using android::base::unique_fd;
53using android::base::WriteStringToFile;
54
55const std::string fb_get_error() {
56    return g_error;
57}
58
59static int64_t check_response(Transport* transport, uint32_t size, char* response) {
60    char status[65];
61
62    while (true) {
63        int r = transport->Read(status, 64);
64        if (r < 0) {
65            g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
66            transport->Close();
67            return -1;
68        }
69        status[r] = 0;
70
71        if (r < 4) {
72            g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
73            transport->Close();
74            return -1;
75        }
76
77        if (!memcmp(status, "INFO", 4)) {
78            fprintf(stderr,"(bootloader) %s\n", status + 4);
79            continue;
80        }
81
82        if (!memcmp(status, "OKAY", 4)) {
83            if (response) {
84                strcpy(response, (char*) status + 4);
85            }
86            return 0;
87        }
88
89        if (!memcmp(status, "FAIL", 4)) {
90            if (r > 4) {
91                g_error = android::base::StringPrintf("remote: %s", status + 4);
92            } else {
93                g_error = "remote failure";
94            }
95            return -1;
96        }
97
98        if (!memcmp(status, "DATA", 4) && size > 0){
99            uint32_t dsize = strtol(status + 4, 0, 16);
100            if (dsize > size) {
101                g_error = android::base::StringPrintf("data size too large (%d)", dsize);
102                transport->Close();
103                return -1;
104            }
105            return dsize;
106        }
107
108        g_error = "unknown status code";
109        transport->Close();
110        break;
111    }
112
113    return -1;
114}
115
116static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
117    size_t cmdsize = strlen(cmd);
118    if (cmdsize > 64) {
119        g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
120        return -1;
121    }
122
123    if (response) {
124        response[0] = 0;
125    }
126
127    if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
128        g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
129        transport->Close();
130        return -1;
131    }
132
133    return check_response(transport, size, response);
134}
135
136static int64_t _command_write_data(Transport* transport, const void* data, uint32_t size) {
137    int64_t r = transport->Write(data, size);
138    if (r < 0) {
139        g_error = android::base::StringPrintf("data write failure (%s)", strerror(errno));
140        transport->Close();
141        return -1;
142    }
143    if (r != static_cast<int64_t>(size)) {
144        g_error = "data write failure (short transfer)";
145        transport->Close();
146        return -1;
147    }
148    return r;
149}
150
151static int64_t _command_read_data(Transport* transport, void* data, uint32_t size) {
152    int64_t r = transport->Read(data, size);
153    if (r < 0) {
154        g_error = android::base::StringPrintf("data read failure (%s)", strerror(errno));
155        transport->Close();
156        return -1;
157    }
158    if (r != (static_cast<int64_t>(size))) {
159        g_error = "data read failure (short transfer)";
160        transport->Close();
161        return -1;
162    }
163    return r;
164}
165
166static int64_t _command_end(Transport* transport) {
167    return check_response(transport, 0, 0) < 0 ? -1 : 0;
168}
169
170static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
171                         char* response) {
172    if (size == 0) {
173        return -1;
174    }
175
176    int64_t r = _command_start(transport, cmd, size, response);
177    if (r < 0) {
178        return -1;
179    }
180    r = _command_write_data(transport, data, size);
181    if (r < 0) {
182        return -1;
183    }
184
185    r = _command_end(transport);
186    if (r < 0) {
187        return -1;
188    }
189
190    return size;
191}
192
193static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
194                                char* response) {
195    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
196    off64_t offset = 0;
197    uint32_t remaining = size;
198
199    if (_command_start(transport, cmd, size, response) < 0) {
200        return -1;
201    }
202
203    while (remaining) {
204        android::FileMap filemap;
205        size_t len = std::min(remaining, MAX_MAP_SIZE);
206
207        if (!filemap.create(NULL, fd, offset, len, true)) {
208            return -1;
209        }
210
211        if (_command_write_data(transport, filemap.getDataPtr(), len) < 0) {
212            return -1;
213        }
214
215        remaining -= len;
216        offset += len;
217    }
218
219    if (_command_end(transport) < 0) {
220        return -1;
221    }
222
223    return size;
224}
225
226static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
227    return _command_start(transport, cmd, 0, response);
228}
229
230int fb_command(Transport* transport, const char* cmd) {
231    return _command_send_no_data(transport, cmd, 0);
232}
233
234int fb_command_response(Transport* transport, const char* cmd, char* response) {
235    return _command_send_no_data(transport, cmd, response);
236}
237
238int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
239    std::string cmd(android::base::StringPrintf("download:%08x", size));
240    return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
241}
242
243int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
244    std::string cmd(android::base::StringPrintf("download:%08x", size));
245    return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
246}
247
248int64_t fb_upload_data(Transport* transport, const char* outfile) {
249    // positive return value is the upload size sent by the device
250    int64_t r = _command_start(transport, "upload", std::numeric_limits<int32_t>::max(), nullptr);
251    if (r <= 0) {
252        g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
253        return r;
254    }
255
256    std::string data;
257    data.resize(r);
258    if ((r = _command_read_data(transport, &data[0], data.size())) == -1) {
259        return r;
260    }
261
262    if (!WriteStringToFile(data, outfile, true)) {
263        g_error = android::base::StringPrintf("write to '%s' failed", outfile);
264        return -1;
265    }
266
267    return _command_end(transport);
268}
269
270#define TRANSPORT_BUF_SIZE 1024
271static char transport_buf[TRANSPORT_BUF_SIZE];
272static int transport_buf_len;
273
274static int fb_download_data_sparse_write(void *priv, const void *data, int len)
275{
276    int r;
277    Transport* transport = reinterpret_cast<Transport*>(priv);
278    int to_write;
279    const char* ptr = reinterpret_cast<const char*>(data);
280
281    if (transport_buf_len) {
282        to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
283
284        memcpy(transport_buf + transport_buf_len, ptr, to_write);
285        transport_buf_len += to_write;
286        ptr += to_write;
287        len -= to_write;
288    }
289
290    if (transport_buf_len == TRANSPORT_BUF_SIZE) {
291        r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
292        if (r != TRANSPORT_BUF_SIZE) {
293            return -1;
294        }
295        transport_buf_len = 0;
296    }
297
298    if (len > TRANSPORT_BUF_SIZE) {
299        if (transport_buf_len > 0) {
300            g_error = "internal error: transport_buf not empty";
301            return -1;
302        }
303        to_write = round_down(len, TRANSPORT_BUF_SIZE);
304        r = _command_write_data(transport, ptr, to_write);
305        if (r != to_write) {
306            return -1;
307        }
308        ptr += to_write;
309        len -= to_write;
310    }
311
312    if (len > 0) {
313        if (len > TRANSPORT_BUF_SIZE) {
314            g_error = "internal error: too much left for transport_buf";
315            return -1;
316        }
317        memcpy(transport_buf, ptr, len);
318        transport_buf_len = len;
319    }
320
321    return 0;
322}
323
324static int fb_download_data_sparse_flush(Transport* transport) {
325    if (transport_buf_len > 0) {
326        int64_t r = _command_write_data(transport, transport_buf, transport_buf_len);
327        if (r != static_cast<int64_t>(transport_buf_len)) {
328            return -1;
329        }
330        transport_buf_len = 0;
331    }
332    return 0;
333}
334
335int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
336    int size = sparse_file_len(s, true, false);
337    if (size <= 0) {
338        return -1;
339    }
340
341    std::string cmd(android::base::StringPrintf("download:%08x", size));
342    int r = _command_start(transport, cmd.c_str(), size, 0);
343    if (r < 0) {
344        return -1;
345    }
346
347    r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
348    if (r < 0) {
349        return -1;
350    }
351
352    r = fb_download_data_sparse_flush(transport);
353    if (r < 0) {
354        return -1;
355    }
356
357    return _command_end(transport);
358}
359