xref: /openbmc/phosphor-ipmi-flash/tools/helper.cpp (revision f450486f238d6402b3ff9680ff50d2987b4f2d3d)
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