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 <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 
37 #include <algorithm>
38 
39 #include <android-base/stringprintf.h>
40 #include <sparse/sparse.h>
41 
42 #include "fastboot.h"
43 #include "transport.h"
44 
45 static std::string g_error;
46 
fb_get_error()47 const std::string fb_get_error() {
48     return g_error;
49 }
50 
check_response(Transport * transport,uint32_t size,char * response)51 static int check_response(Transport* transport, uint32_t size, char* response) {
52     char status[65];
53 
54     while (true) {
55         int r = transport->Read(status, 64);
56         if (r < 0) {
57             g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
58             transport->Close();
59             return -1;
60         }
61         status[r] = 0;
62 
63         if (r < 4) {
64             g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
65             transport->Close();
66             return -1;
67         }
68 
69         if (!memcmp(status, "INFO", 4)) {
70             fprintf(stderr,"(bootloader) %s\n", status + 4);
71             continue;
72         }
73 
74         if (!memcmp(status, "OKAY", 4)) {
75             if (response) {
76                 strcpy(response, (char*) status + 4);
77             }
78             return 0;
79         }
80 
81         if (!memcmp(status, "FAIL", 4)) {
82             if (r > 4) {
83                 g_error = android::base::StringPrintf("remote: %s", status + 4);
84             } else {
85                 g_error = "remote failure";
86             }
87             return -1;
88         }
89 
90         if (!memcmp(status, "DATA", 4) && size > 0){
91             uint32_t dsize = strtol(status + 4, 0, 16);
92             if (dsize > size) {
93                 g_error = android::base::StringPrintf("data size too large (%d)", dsize);
94                 transport->Close();
95                 return -1;
96             }
97             return dsize;
98         }
99 
100         g_error = "unknown status code";
101         transport->Close();
102         break;
103     }
104 
105     return -1;
106 }
107 
_command_start(Transport * transport,const char * cmd,uint32_t size,char * response)108 static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
109     size_t cmdsize = strlen(cmd);
110     if (cmdsize > 64) {
111         g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
112         return -1;
113     }
114 
115     if (response) {
116         response[0] = 0;
117     }
118 
119     if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
120         g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
121         transport->Close();
122         return -1;
123     }
124 
125     return check_response(transport, size, response);
126 }
127 
_command_data(Transport * transport,const void * data,uint32_t size)128 static int _command_data(Transport* transport, const void* data, uint32_t size) {
129     int r = transport->Write(data, size);
130     if (r < 0) {
131         g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
132         transport->Close();
133         return -1;
134     }
135     if (r != ((int) size)) {
136         g_error = "data transfer failure (short transfer)";
137         transport->Close();
138         return -1;
139     }
140     return r;
141 }
142 
_command_end(Transport * transport)143 static int _command_end(Transport* transport) {
144     return check_response(transport, 0, 0) < 0 ? -1 : 0;
145 }
146 
_command_send(Transport * transport,const char * cmd,const void * data,uint32_t size,char * response)147 static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
148                          char* response) {
149     if (size == 0) {
150         return -1;
151     }
152 
153     int r = _command_start(transport, cmd, size, response);
154     if (r < 0) {
155         return -1;
156     }
157 
158     r = _command_data(transport, data, size);
159     if (r < 0) {
160         return -1;
161     }
162 
163     r = _command_end(transport);
164     if (r < 0) {
165         return -1;
166     }
167 
168     return size;
169 }
170 
_command_send_no_data(Transport * transport,const char * cmd,char * response)171 static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
172     return _command_start(transport, cmd, 0, response);
173 }
174 
fb_command(Transport * transport,const char * cmd)175 int fb_command(Transport* transport, const char* cmd) {
176     return _command_send_no_data(transport, cmd, 0);
177 }
178 
fb_command_response(Transport * transport,const char * cmd,char * response)179 int fb_command_response(Transport* transport, const char* cmd, char* response) {
180     return _command_send_no_data(transport, cmd, response);
181 }
182 
fb_download_data(Transport * transport,const void * data,uint32_t size)183 int fb_download_data(Transport* transport, const void* data, uint32_t size) {
184     char cmd[64];
185     snprintf(cmd, sizeof(cmd), "download:%08x", size);
186     return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
187 }
188 
189 #define TRANSPORT_BUF_SIZE 1024
190 static char transport_buf[TRANSPORT_BUF_SIZE];
191 static int transport_buf_len;
192 
fb_download_data_sparse_write(void * priv,const void * data,int len)193 static int fb_download_data_sparse_write(void *priv, const void *data, int len)
194 {
195     int r;
196     Transport* transport = reinterpret_cast<Transport*>(priv);
197     int to_write;
198     const char* ptr = reinterpret_cast<const char*>(data);
199 
200     if (transport_buf_len) {
201         to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
202 
203         memcpy(transport_buf + transport_buf_len, ptr, to_write);
204         transport_buf_len += to_write;
205         ptr += to_write;
206         len -= to_write;
207     }
208 
209     if (transport_buf_len == TRANSPORT_BUF_SIZE) {
210         r = _command_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
211         if (r != TRANSPORT_BUF_SIZE) {
212             return -1;
213         }
214         transport_buf_len = 0;
215     }
216 
217     if (len > TRANSPORT_BUF_SIZE) {
218         if (transport_buf_len > 0) {
219             g_error = "internal error: transport_buf not empty";
220             return -1;
221         }
222         to_write = round_down(len, TRANSPORT_BUF_SIZE);
223         r = _command_data(transport, ptr, to_write);
224         if (r != to_write) {
225             return -1;
226         }
227         ptr += to_write;
228         len -= to_write;
229     }
230 
231     if (len > 0) {
232         if (len > TRANSPORT_BUF_SIZE) {
233             g_error = "internal error: too much left for transport_buf";
234             return -1;
235         }
236         memcpy(transport_buf, ptr, len);
237         transport_buf_len = len;
238     }
239 
240     return 0;
241 }
242 
fb_download_data_sparse_flush(Transport * transport)243 static int fb_download_data_sparse_flush(Transport* transport) {
244     if (transport_buf_len > 0) {
245         if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
246             return -1;
247         }
248         transport_buf_len = 0;
249     }
250     return 0;
251 }
252 
fb_download_data_sparse(Transport * transport,struct sparse_file * s)253 int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
254     int size = sparse_file_len(s, true, false);
255     if (size <= 0) {
256         return -1;
257     }
258 
259     char cmd[64];
260     snprintf(cmd, sizeof(cmd), "download:%08x", size);
261     int r = _command_start(transport, cmd, size, 0);
262     if (r < 0) {
263         return -1;
264     }
265 
266     r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
267     if (r < 0) {
268         return -1;
269     }
270 
271     r = fb_download_data_sparse_flush(transport);
272     if (r < 0) {
273         return -1;
274     }
275 
276     return _command_end(transport);
277 }
278