1 /*
2 * Copyright (C) 2018 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 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <termios.h>
36 #include <unistd.h>
37 #include <chrono>
38 #include <cstdlib>
39 #include <fstream>
40 #include <map>
41 #include <random>
42 #include <regex>
43 #include <set>
44 #include <thread>
45 #include <vector>
46
47 #include <android-base/stringprintf.h>
48 #include <gtest/gtest.h>
49
50 #include "fastboot_driver.h"
51 #include "usb.h"
52
53 #include "extensions.h"
54 #include "fixtures.h"
55 #include "test_utils.h"
56 #include "usb_transport_sniffer.h"
57
58 using namespace std::literals::chrono_literals;
59
60 namespace fastboot {
61
MatchFastboot(usb_ifc_info * info,const std::string & local_serial)62 int FastBootTest::MatchFastboot(usb_ifc_info* info, const std::string& local_serial) {
63 if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
64 return -1;
65 }
66
67 cb_scratch = info->device_path;
68
69 // require matching serial number or device path if requested
70 // at the command line with the -s option.
71 if (!local_serial.empty() && local_serial != info->serial_number &&
72 local_serial != info->device_path)
73 return -1;
74 return 0;
75 }
76
UsbStillAvailible()77 bool FastBootTest::UsbStillAvailible() {
78 // For some reason someone decided to prefix the path with "usb:"
79 std::string prefix("usb:");
80 if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {
81 std::string fname(device_path.begin() + prefix.size(), device_path.end());
82 std::string real_path =
83 android::base::StringPrintf("/sys/bus/usb/devices/%s/serial", fname.c_str());
84 std::ifstream f(real_path.c_str());
85 return f.good();
86 }
87 exit(-1); // This should never happen
88 return true;
89 }
90
UserSpaceFastboot()91 bool FastBootTest::UserSpaceFastboot() {
92 std::string value;
93 fb->GetVar("is-userspace", &value);
94 return value == "yes";
95 }
96
DownloadCommand(uint32_t size,std::string * response,std::vector<std::string> * info)97 RetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,
98 std::vector<std::string>* info) {
99 return fb->DownloadCommand(size, response, info);
100 }
101
SendBuffer(const std::vector<char> & buf)102 RetCode FastBootTest::SendBuffer(const std::vector<char>& buf) {
103 return fb->SendBuffer(buf);
104 }
105
HandleResponse(std::string * response,std::vector<std::string> * info,int * dsize)106 RetCode FastBootTest::HandleResponse(std::string* response, std::vector<std::string>* info,
107 int* dsize) {
108 return fb->HandleResponse(response, info, dsize);
109 }
110
SetUp()111 void FastBootTest::SetUp() {
112 if (device_path != "") { // make sure the device is still connected
113 ASSERT_TRUE(UsbStillAvailible()); // The device disconnected
114 }
115
116 const auto matcher = [](usb_ifc_info* info) -> int {
117 return MatchFastboot(info, device_serial);
118 };
119 for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
120 std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
121 if (usb)
122 transport = std::unique_ptr<UsbTransportSniffer>(
123 new UsbTransportSniffer(std::move(usb), serial_port));
124 std::this_thread::sleep_for(std::chrono::milliseconds(10));
125 }
126
127 ASSERT_TRUE(transport); // no nullptr
128
129 if (device_path == "") { // We set it the first time, then make sure it never changes
130 device_path = cb_scratch;
131 } else {
132 ASSERT_EQ(device_path, cb_scratch); // The path can not change
133 }
134 fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
135 // No error checking since non-A/B devices may not support the command
136 fb->GetVar("current-slot", &initial_slot);
137 }
138
TearDown()139 void FastBootTest::TearDown() {
140 EXPECT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
141 // No error checking since non-A/B devices may not support the command
142 fb->SetActive(initial_slot);
143
144 TearDownSerial();
145
146 fb.reset();
147
148 if (transport) {
149 transport.reset();
150 }
151
152 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
153 }
154
155 // TODO, this should eventually be piped to a file instead of stdout
TearDownSerial()156 void FastBootTest::TearDownSerial() {
157 if (!transport) return;
158 // One last read from serial
159 transport->ProcessSerial();
160 if (HasFailure()) {
161 // TODO, print commands leading up
162 printf("<<<<<<<< TRACE BEGIN >>>>>>>>>\n");
163 printf("%s", transport->CreateTrace().c_str());
164 printf("<<<<<<<< TRACE END >>>>>>>>>\n");
165 // std::vector<std::pair<const TransferType, const std::vector<char>>> prev =
166 // transport->Transfers();
167 }
168 }
169
ReconnectFastbootDevice()170 void FastBootTest::ReconnectFastbootDevice() {
171 fb.reset();
172 transport.reset();
173 while (UsbStillAvailible())
174 ;
175 printf("WAITING FOR DEVICE\n");
176 // Need to wait for device
177 const auto matcher = [](usb_ifc_info* info) -> int {
178 return MatchFastboot(info, device_serial);
179 };
180 while (!transport) {
181 std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
182 if (usb) {
183 transport = std::unique_ptr<UsbTransportSniffer>(
184 new UsbTransportSniffer(std::move(usb), serial_port));
185 }
186 std::this_thread::sleep_for(1s);
187 }
188 device_path = cb_scratch;
189 fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
190 }
191
SetLockState(bool unlock,bool assert_change)192 void FastBootTest::SetLockState(bool unlock, bool assert_change) {
193 if (!fb) {
194 return;
195 }
196
197 // User space fastboot implementations are not allowed to communicate to
198 // secure hardware and hence cannot lock/unlock the device.
199 if (UserSpaceFastboot()) {
200 return;
201 }
202
203 std::string resp;
204 std::vector<std::string> info;
205 // To avoid risk of bricking device, make sure unlock ability is set to 1
206 ASSERT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
207 << "'flashing get_unlock_ability' failed";
208
209 // There are two ways this can be reported, through info or the actual response
210 if (!resp.empty()) { // must be in the info response
211 ASSERT_EQ(resp.back(), '1')
212 << "Unlock ability must be set to 1 to avoid bricking device, see "
213 "'https://source.android.com/devices/bootloader/unlock-trusty'";
214 } else {
215 ASSERT_FALSE(info.empty()) << "'flashing get_unlock_ability' returned empty response";
216 ASSERT_FALSE(info.back().empty()) << "Expected non-empty info response";
217 ASSERT_EQ(info.back().back(), '1')
218 << "Unlock ability must be set to 1 to avoid bricking device, see "
219 "'https://source.android.com/devices/bootloader/unlock-trusty'";
220 }
221
222 EXPECT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
223 ASSERT_TRUE(resp == "no" || resp == "yes")
224 << "getvar:unlocked response was not 'no' or 'yes': " + resp;
225
226 if ((unlock && resp == "no") || (!unlock && resp == "yes")) {
227 std::string cmd = unlock ? "unlock" : "lock";
228 ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
229 << "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
230 printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
231 ReconnectFastbootDevice();
232 if (assert_change) {
233 ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
234 ASSERT_EQ(resp, unlock ? "yes" : "no")
235 << "getvar:unlocked response was not 'no' or 'yes': " + resp;
236 }
237 printf("SUCCESS\n");
238 }
239 }
240
241 std::string FastBootTest::device_path = "";
242 std::string FastBootTest::cb_scratch = "";
243 std::string FastBootTest::initial_slot = "";
244 int FastBootTest::serial_port = 0;
245 std::string FastBootTest::device_serial = "";
246
247 template <bool UNLOCKED>
SetUp()248 void ModeTest<UNLOCKED>::SetUp() {
249 ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
250 ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
251 }
252 // Need to instatiate it, so linker can find it later
253 template class ModeTest<true>;
254 template class ModeTest<false>;
255
TearDown()256 void Fuzz::TearDown() {
257 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
258
259 TearDownSerial();
260
261 std::string tmp;
262 if (fb->GetVar("product", &tmp) != SUCCESS) {
263 printf("DEVICE UNRESPONSE, attempting to recover...");
264 transport->Reset();
265 printf("issued USB reset...");
266
267 if (fb->GetVar("product", &tmp) != SUCCESS) {
268 printf("FAIL\n");
269 exit(-1);
270 }
271 printf("SUCCESS!\n");
272 }
273
274 if (transport) {
275 transport.reset();
276 }
277
278 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
279 }
280
281 template <bool UNLOCKED>
SetUp()282 void ExtensionsPartition<UNLOCKED>::SetUp() {
283 ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
284 ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
285
286 if (!fb) {
287 return;
288 }
289 const std::string name = GetParam().first;
290
291 std::string var;
292 ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
293 int32_t num_slots = strtol(var.c_str(), nullptr, 10);
294 real_parts = GeneratePartitionNames(name, GetParam().second.slots ? num_slots : 0);
295
296 ASSERT_EQ(fb->GetVar("partition-size:" + real_parts.front(), &var), SUCCESS)
297 << "Getting partition size failed";
298 part_size = strtoll(var.c_str(), nullptr, 16);
299 ASSERT_GT(part_size, 0) << "Partition size reported was invalid";
300
301 ASSERT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "Getting max download size failed";
302 max_dl = strtoll(var.c_str(), nullptr, 16);
303 ASSERT_GT(max_dl, 0) << "Max download size reported was invalid";
304
305 max_flash = std::min(part_size, max_dl);
306 }
307 template class ExtensionsPartition<true>;
308 template class ExtensionsPartition<false>;
309
310 } // end namespace fastboot
311