blob: 280cfb691f524603578b77539c17eaec68583bde [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <chrono>
#include <cstdlib>
#include <fstream>
#include <map>
#include <random>
#include <regex>
#include <set>
#include <thread>
#include <vector>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include "fastboot_driver.h"
#include "usb.h"
#include "extensions.h"
#include "fixtures.h"
#include "test_utils.h"
#include "usb_transport_sniffer.h"
namespace fastboot {
int FastBootTest::MatchFastboot(usb_ifc_info* info, const char* local_serial) {
if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
return -1;
}
cb_scratch = info->device_path;
// require matching serial number or device path if requested
// at the command line with the -s option.
if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
strcmp(local_serial, info->device_path) != 0))
return -1;
return 0;
}
bool FastBootTest::UsbStillAvailible() {
// For some reason someone decided to prefix the path with "usb:"
std::string prefix("usb:");
if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {
std::string fname(device_path.begin() + prefix.size(), device_path.end());
std::string real_path =
android::base::StringPrintf("/sys/bus/usb/devices/%s/serial", fname.c_str());
std::ifstream f(real_path.c_str());
return f.good();
}
exit(-1); // This should never happen
return true;
}
bool FastBootTest::UserSpaceFastboot() {
std::string value;
fb->GetVar("is-userspace", &value);
return value == "yes";
}
RetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,
std::vector<std::string>* info) {
return fb->DownloadCommand(size, response, info);
}
RetCode FastBootTest::SendBuffer(const std::vector<char>& buf) {
return fb->SendBuffer(buf);
}
RetCode FastBootTest::HandleResponse(std::string* response, std::vector<std::string>* info,
int* dsize) {
return fb->HandleResponse(response, info, dsize);
}
void FastBootTest::SetUp() {
if (device_path != "") { // make sure the device is still connected
ASSERT_TRUE(UsbStillAvailible()); // The device disconnected
}
const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
if (usb)
transport = std::unique_ptr<UsbTransportSniffer>(
new UsbTransportSniffer(std::move(usb), serial_port));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
ASSERT_TRUE(transport); // no nullptr
if (device_path == "") { // We set it the first time, then make sure it never changes
device_path = cb_scratch;
} else {
ASSERT_EQ(device_path, cb_scratch); // The path can not change
}
fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
}
void FastBootTest::TearDown() {
EXPECT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
TearDownSerial();
fb.reset();
if (transport) {
transport.reset();
}
ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
}
// TODO, this should eventually be piped to a file instead of stdout
void FastBootTest::TearDownSerial() {
if (!transport) return;
// One last read from serial
transport->ProcessSerial();
if (HasFailure()) {
// TODO, print commands leading up
printf("<<<<<<<< TRACE BEGIN >>>>>>>>>\n");
printf("%s", transport->CreateTrace().c_str());
printf("<<<<<<<< TRACE END >>>>>>>>>\n");
// std::vector<std::pair<const TransferType, const std::vector<char>>> prev =
// transport->Transfers();
}
}
void FastBootTest::SetLockState(bool unlock, bool assert_change) {
if (!fb) {
return;
}
// User space fastboot implementations are not allowed to communicate to
// secure hardware and hence cannot lock/unlock the device.
if (UserSpaceFastboot()) {
return;
}
std::string resp;
std::vector<std::string> info;
// To avoid risk of bricking device, make sure unlock ability is set to 1
ASSERT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
<< "'flashing get_unlock_ability' failed";
// There are two ways this can be reported, through info or the actual response
if (!resp.empty()) { // must be in the info response
ASSERT_EQ(resp.back(), '1')
<< "Unlock ability must be set to 1 to avoid bricking device, see "
"'https://source.android.com/devices/bootloader/unlock-trusty'";
} else {
ASSERT_FALSE(info.empty()) << "'flashing get_unlock_ability' returned empty response";
ASSERT_FALSE(info.back().empty()) << "Expected non-empty info response";
ASSERT_EQ(info.back().back(), '1')
<< "Unlock ability must be set to 1 to avoid bricking device, see "
"'https://source.android.com/devices/bootloader/unlock-trusty'";
}
EXPECT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
ASSERT_TRUE(resp == "no" || resp == "yes")
<< "getvar:unlocked response was not 'no' or 'yes': " + resp;
if ((unlock && resp == "no") || (!unlock && resp == "yes")) {
std::string cmd = unlock ? "unlock" : "lock";
ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
<< "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
fb.reset();
transport.reset();
printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
while (UsbStillAvailible())
; // Wait for disconnect
printf("WAITING FOR DEVICE");
// Need to wait for device
const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
while (!transport) {
std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
if (usb) {
transport = std::unique_ptr<UsbTransportSniffer>(
new UsbTransportSniffer(std::move(usb), serial_port));
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
putchar('.');
}
device_path = cb_scratch;
fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
if (assert_change) {
ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
ASSERT_EQ(resp, unlock ? "yes" : "no")
<< "getvar:unlocked response was not 'no' or 'yes': " + resp;
}
printf("SUCCESS\n");
}
}
std::string FastBootTest::device_path = "";
std::string FastBootTest::cb_scratch = "";
int FastBootTest::serial_port = 0;
template <bool UNLOCKED>
void ModeTest<UNLOCKED>::SetUp() {
ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
}
// Need to instatiate it, so linker can find it later
template class ModeTest<true>;
template class ModeTest<false>;
void Fuzz::TearDown() {
ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
TearDownSerial();
std::string tmp;
if (fb->GetVar("product", &tmp) != SUCCESS) {
printf("DEVICE UNRESPONSE, attempting to recover...");
transport->Reset();
printf("issued USB reset...");
if (fb->GetVar("product", &tmp) != SUCCESS) {
printf("FAIL\n");
exit(-1);
}
printf("SUCCESS!\n");
}
if (transport) {
transport.reset();
}
ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
}
template <bool UNLOCKED>
void ExtensionsPartition<UNLOCKED>::SetUp() {
ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
if (!fb) {
return;
}
const std::string name = GetParam().first;
std::string var;
ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
int32_t num_slots = strtol(var.c_str(), nullptr, 10);
real_parts = GeneratePartitionNames(name, GetParam().second.slots ? num_slots : 0);
ASSERT_EQ(fb->GetVar("partition-size:" + real_parts.front(), &var), SUCCESS)
<< "Getting partition size failed";
part_size = strtoll(var.c_str(), nullptr, 16);
ASSERT_GT(part_size, 0) << "Partition size reported was invalid";
ASSERT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "Getting max download size failed";
max_dl = strtoll(var.c_str(), nullptr, 16);
ASSERT_GT(max_dl, 0) << "Max download size reported was invalid";
max_flash = std::min(part_size, max_dl);
}
template class ExtensionsPartition<true>;
template class ExtensionsPartition<false>;
} // end namespace fastboot