1 /* 2 * Copyright 2019 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "helper.hpp" 18 19 #include "status.hpp" 20 #include "tool_errors.hpp" 21 22 #include <ipmiblob/blob_errors.hpp> 23 #include <ipmiblob/blob_interface.hpp> 24 25 #include <algorithm> 26 #include <chrono> 27 #include <optional> 28 #include <thread> 29 #include <utility> 30 31 namespace host_tool 32 { 33 34 template <typename Check> 35 static auto pollStat(std::uint16_t session, ipmiblob::BlobInterface* blob, 36 Check&& check) 37 { 38 using namespace std::chrono_literals; 39 40 constexpr auto maxSleep = 1s; 41 constexpr auto printInterval = 30s; 42 constexpr auto timeout = 30min; 43 44 try 45 { 46 auto start = std::chrono::steady_clock::now(); 47 auto last_print = start; 48 auto last_check = start; 49 auto check_interval = 50ms; 50 51 while (true) 52 { 53 ipmiblob::StatResponse resp = blob->getStat(session); 54 auto ret = check(resp); 55 if (ret.has_value()) 56 { 57 std::fprintf(stderr, "success\n"); 58 return std::move(*ret); 59 } 60 61 auto cur = std::chrono::steady_clock::now(); 62 if (cur - last_print >= printInterval) 63 { 64 std::fprintf(stderr, "running\n"); 65 last_print = cur; 66 } 67 68 auto sleep = check_interval - (cur - last_check); 69 last_check = cur; 70 // Check that we don't timeout immediately after sleeping 71 // to avoid an extra sleep without checking 72 if (cur - start > timeout - sleep) 73 { 74 throw ToolException("Stat check timed out"); 75 } 76 check_interval = std::min<decltype(check_interval)>( 77 check_interval * 2, maxSleep); 78 std::this_thread::sleep_for(sleep); 79 } 80 } 81 catch (const ipmiblob::BlobException& b) 82 { 83 throw ToolException("blob exception received: " + 84 std::string(b.what())); 85 } 86 } 87 88 /* Poll an open verification session. Handling closing the session is not yet 89 * owned by this method. 90 */ 91 void pollStatus(std::uint16_t session, ipmiblob::BlobInterface* blob) 92 { 93 pollStat(session, blob, 94 [](const ipmiblob::StatResponse& resp) -> std::optional<bool> { 95 if (resp.metadata.size() != 1) 96 { 97 throw ToolException("Invalid stat metadata"); 98 } 99 auto result = 100 static_cast<ipmi_flash::ActionStatus>(resp.metadata[0]); 101 switch (result) 102 { 103 case ipmi_flash::ActionStatus::failed: 104 throw ToolException("BMC reported failure"); 105 case ipmi_flash::ActionStatus::unknown: 106 case ipmi_flash::ActionStatus::running: 107 return std::nullopt; 108 case ipmi_flash::ActionStatus::success: 109 return true; 110 default: 111 throw ToolException("Unrecognized action status"); 112 } 113 }); 114 } 115 116 /* Poll an open blob session for reading. 117 * 118 * The committing bit indicates that the blob is not available for reading now 119 * and the reader might come back and check the state later. 120 * 121 * Polling finishes under the following conditions: 122 * - The open_read bit set -> stat successful 123 * - The open_read and committing bits unset -> stat failed; 124 * - Blob exception was received; 125 * - Time ran out. 126 * Polling continues when the open_read bit unset and committing bit set. 127 * If the blob is not open_read and not committing, then it is an error to the 128 * reader. 129 */ 130 uint32_t pollReadReady(std::uint16_t session, ipmiblob::BlobInterface* blob) 131 { 132 return pollStat( 133 session, blob, 134 [](const ipmiblob::StatResponse& resp) -> std::optional<uint32_t> { 135 if (resp.blob_state & ipmiblob::StateFlags::open_read) 136 { 137 return resp.size; 138 } 139 if (resp.blob_state & ipmiblob::StateFlags::committing) 140 { 141 return std::nullopt; 142 } 143 throw ToolException("BMC blob failed to become ready"); 144 }); 145 } 146 147 void* memcpyAligned(void* destination, const void* source, std::size_t size) 148 { 149 std::size_t i = 0; 150 std::size_t bytesCopied = 0; 151 152 if (!(reinterpret_cast<std::uintptr_t>(destination) % 153 sizeof(std::uint64_t)) && 154 !(reinterpret_cast<std::uintptr_t>(source) % sizeof(std::uint64_t))) 155 { 156 auto src64 = reinterpret_cast<const volatile std::uint64_t*>(source); 157 auto dest64 = reinterpret_cast<volatile std::uint64_t*>(destination); 158 159 for (i = 0; i < size / sizeof(std::uint64_t); i++) 160 { 161 *dest64++ = *src64++; 162 bytesCopied += sizeof(std::uint64_t); 163 } 164 } 165 166 auto srcMem8 = 167 reinterpret_cast<const volatile std::uint8_t*>(source) + bytesCopied; 168 auto destMem8 = 169 reinterpret_cast<volatile std::uint8_t*>(destination) + bytesCopied; 170 171 for (i = bytesCopied; i < size; i++) 172 { 173 *destMem8++ = *srcMem8++; 174 } 175 176 return destination; 177 } 178 179 } // namespace host_tool 180