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