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 <chrono> 26 #include <thread> 27 #include <utility> 28 29 namespace host_tool 30 { 31 32 /* Poll an open verification session. Handling closing the session is not yet 33 * owned by this method. 34 */ 35 void pollStatus(std::uint16_t session, ipmiblob::BlobInterface* blob) 36 { 37 using namespace std::chrono_literals; 38 39 static constexpr auto verificationSleep = 5s; 40 ipmi_flash::ActionStatus result = ipmi_flash::ActionStatus::unknown; 41 42 try 43 { 44 /* sleep for 5 seconds and check 360 times, for a timeout of: 1800 45 * seconds (30 minutes). 46 * TODO: make this command line configurable and provide smaller 47 * default value. 48 */ 49 static constexpr int commandAttempts = 360; 50 int attempts = 0; 51 bool exitLoop = false; 52 53 /* Reach back the current status from the verification service output. 54 */ 55 while (attempts++ < commandAttempts) 56 { 57 ipmiblob::StatResponse resp = blob->getStat(session); 58 59 if (resp.metadata.size() != sizeof(std::uint8_t)) 60 { 61 /* TODO: How do we want to handle the verification failures, 62 * because closing the session to the verify blob has a special 63 * as-of-yet not fully defined behavior. 64 */ 65 std::fprintf(stderr, "Received invalid metadata response!!!\n"); 66 } 67 68 result = static_cast<ipmi_flash::ActionStatus>(resp.metadata[0]); 69 70 switch (result) 71 { 72 case ipmi_flash::ActionStatus::failed: 73 std::fprintf(stderr, "failed\n"); 74 exitLoop = true; 75 break; 76 case ipmi_flash::ActionStatus::unknown: 77 std::fprintf(stderr, "other\n"); 78 break; 79 case ipmi_flash::ActionStatus::running: 80 std::fprintf(stderr, "running\n"); 81 break; 82 case ipmi_flash::ActionStatus::success: 83 std::fprintf(stderr, "success\n"); 84 exitLoop = true; 85 break; 86 default: 87 std::fprintf(stderr, "wat\n"); 88 } 89 90 if (exitLoop) 91 { 92 break; 93 } 94 std::this_thread::sleep_for(verificationSleep); 95 } 96 } 97 catch (const ipmiblob::BlobException& b) 98 { 99 throw ToolException("blob exception received: " + 100 std::string(b.what())); 101 } 102 103 /* TODO: If this is reached and it's not success, it may be worth just 104 * throwing a ToolException with a timeout message specifying the final 105 * read's value. 106 * 107 * TODO: Given that excepting from certain points leaves the BMC update 108 * state machine in an inconsistent state, we need to carefully evaluate 109 * which exceptions from the lower layers allow one to try and delete the 110 * blobs to rollback the state and progress. 111 */ 112 if (result != ipmi_flash::ActionStatus::success) 113 { 114 throw ToolException("BMC reported failure"); 115 } 116 } 117 118 /* Poll an open blob session for reading. 119 * 120 * The committing bit indicates that the blob is not available for reading now 121 * and the reader might come back and check the state later. 122 * 123 * Polling finishes under the following conditions: 124 * - The open_read bit set -> stat successful 125 * - The open_read and committing bits unset -> stat failed; 126 * - Blob exception was received; 127 * - Time ran out. 128 * Polling continues when the open_read bit unset and committing bit set. 129 * If the blob is not open_read and not committing, then it is an error to the 130 * reader. 131 */ 132 uint32_t pollReadReady(std::uint16_t session, ipmiblob::BlobInterface* blob) 133 { 134 using namespace std::chrono_literals; 135 static constexpr auto pollingSleep = 5s; 136 ipmiblob::StatResponse blobStatResp; 137 138 try 139 { 140 /* Polling lasts 5 minutes. When opening a version blob, the system 141 * unit defined in the version handler will extract the running version 142 * from the image on the flash. 143 */ 144 static constexpr int commandAttempts = 60; 145 int attempts = 0; 146 147 while (attempts++ < commandAttempts) 148 { 149 blobStatResp = blob->getStat(session); 150 151 if (blobStatResp.blob_state & ipmiblob::StateFlags::open_read) 152 { 153 std::fprintf(stderr, "success\n"); 154 return blobStatResp.size; 155 } 156 else if (blobStatResp.blob_state & ipmiblob::StateFlags::committing) 157 { 158 std::fprintf(stderr, "running\n"); 159 } 160 else 161 { 162 std::fprintf(stderr, "failed\n"); 163 throw ToolException("BMC reported failure"); 164 } 165 166 std::this_thread::sleep_for(pollingSleep); 167 } 168 } 169 catch (const ipmiblob::BlobException& b) 170 { 171 throw ToolException("blob exception received: " + 172 std::string(b.what())); 173 } 174 175 throw ToolException("Timed out waiting for BMC read ready"); 176 } 177 178 void* memcpyAligned(void* destination, const void* source, std::size_t size) 179 { 180 std::size_t i = 0; 181 std::size_t bytesCopied = 0; 182 183 if (!(reinterpret_cast<std::uintptr_t>(destination) % 184 sizeof(std::uint64_t)) && 185 !(reinterpret_cast<std::uintptr_t>(source) % sizeof(std::uint64_t))) 186 { 187 auto src64 = reinterpret_cast<const volatile std::uint64_t*>(source); 188 auto dest64 = reinterpret_cast<volatile std::uint64_t*>(destination); 189 190 for (i = 0; i < size / sizeof(std::uint64_t); i++) 191 { 192 *dest64++ = *src64++; 193 bytesCopied += sizeof(std::uint64_t); 194 } 195 } 196 197 auto srcMem8 = 198 reinterpret_cast<const volatile std::uint8_t*>(source) + bytesCopied; 199 auto destMem8 = 200 reinterpret_cast<volatile std::uint8_t*>(destination) + bytesCopied; 201 202 for (i = bytesCopied; i < size; i++) 203 { 204 *destMem8++ = *srcMem8++; 205 } 206 207 return destination; 208 } 209 210 } // namespace host_tool 211