xref: /openbmc/phosphor-ipmi-flash/tools/helper.cpp (revision fd16f6dbb0ca64263ed30e6ce18d0c0d53e51bbe)
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 bool 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     return (result == ipmi_flash::ActionStatus::success);
113 }
114 
115 /* Poll an open blob session for reading.
116  *
117  * The committing bit indicates that the blob is not available for reading now
118  * and the reader might come back and check the state later.
119  *
120  * Polling finishes under the following conditions:
121  * - The open_read bit set -> stat successful
122  * - The open_read and committing bits unset -> stat failed;
123  * - Blob exception was received;
124  * - Time ran out.
125  * Polling continues when the open_read bit unset and committing bit set.
126  * If the blob is not open_read and not committing, then it is an error to the
127  * reader.
128  */
129 std::pair<bool, uint32_t> pollReadReady(std::uint16_t session,
130                                         ipmiblob::BlobInterface* blob)
131 {
132     using namespace std::chrono_literals;
133     static constexpr auto pollingSleep = 5s;
134     ipmiblob::StatResponse blobStatResp;
135 
136     try
137     {
138         /* Polling lasts 5 minutes. When opening a version blob, the system
139          * unit defined in the version handler will extract the running version
140          * from the image on the flash.
141          */
142         static constexpr int commandAttempts = 60;
143         int attempts = 0;
144 
145         while (attempts++ < commandAttempts)
146         {
147             blobStatResp = blob->getStat(session);
148 
149             if (blobStatResp.blob_state & ipmiblob::StateFlags::open_read)
150             {
151                 std::fprintf(stderr, "success\n");
152                 return std::make_pair(true, blobStatResp.size);
153             }
154             else if (blobStatResp.blob_state & ipmiblob::StateFlags::committing)
155             {
156                 std::fprintf(stderr, "running\n");
157             }
158             else
159             {
160                 std::fprintf(stderr, "failed\n");
161                 return std::make_pair(false, 0);
162             }
163 
164             std::this_thread::sleep_for(pollingSleep);
165         }
166     }
167     catch (const ipmiblob::BlobException& b)
168     {
169         throw ToolException("blob exception received: " +
170                             std::string(b.what()));
171     }
172 
173     return std::make_pair(false, 0);
174 }
175 
176 void* memcpyAligned(void* destination, const void* source, std::size_t size)
177 {
178     std::size_t i = 0;
179     std::size_t bytesCopied = 0;
180 
181     if (!(reinterpret_cast<std::uintptr_t>(destination) %
182           sizeof(std::uint64_t)) &&
183         !(reinterpret_cast<std::uintptr_t>(source) % sizeof(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