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>
pollStat(std::uint16_t session,ipmiblob::BlobInterface * blob,Check && 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(
84             "blob exception received: " + 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  */
pollStatus(std::uint16_t session,ipmiblob::BlobInterface * blob)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  */
pollReadReady(std::uint16_t session,ipmiblob::BlobInterface * blob)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 
memcpyAligned(void * destination,const void * source,std::size_t size)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 = reinterpret_cast<const volatile std::uint8_t*>(source) +
167                    bytesCopied;
168     auto destMem8 = reinterpret_cast<volatile std::uint8_t*>(destination) +
169                     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