1 /*
2 // Copyright (c) 2019 Intel Corporation
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 #pragma once
17 #include <fcntl.h>
18 #include <unistd.h>
19 
20 #include <cereal/access.hpp>
21 #include <cereal/archives/json.hpp>
22 #include <cereal/cereal.hpp>
23 #include <cereal/types/map.hpp>
24 #include <cereal/types/tuple.hpp>
25 #include <cereal/types/vector.hpp>
26 #include <chrono>
27 #include <experimental/filesystem>
28 #include <fstream>
29 #include <iostream>
30 #include <phosphor-logging/elog-errors.hpp>
31 #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp>
32 #include <xyz/openbmc_project/Common/error.hpp>
33 #include <xyz/openbmc_project/State/Boot/PostCode/server.hpp>
34 #include <xyz/openbmc_project/State/Host/server.hpp>
35 
36 #define MaxPostCodeCycles 100
37 
38 const static constexpr char *CurrentBootCycleCountName =
39     "CurrentBootCycleCount";
40 const static constexpr char *CurrentBootCycleIndexName =
41     "CurrentBootCycleIndex";
42 
43 // Singleton holder to store host/node and other path information
44 class PostCodeDataHolder
45 {
46     static PostCodeDataHolder *instance;
47 
48     PostCodeDataHolder()
49     {
50     }
51 
52   public:
53     static PostCodeDataHolder *getInstance()
54     {
55         if (!instance)
56             instance = new PostCodeDataHolder;
57         return instance;
58     }
59 
60     int node;
61 
62     const static constexpr char *PostCodePath =
63         "/xyz/openbmc_project/state/boot/raw";
64     const static constexpr char *PropertiesIntf =
65         "org.freedesktop.DBus.Properties";
66     const static constexpr char *PostCodeListPathPrefix =
67         "/var/lib/phosphor-post-code-manager/host";
68     const static constexpr char *HostStatePathPrefix =
69         "/xyz/openbmc_project/state/host";
70 };
71 
72 struct EventDeleter
73 {
74     void operator()(sd_event *event) const
75     {
76         event = sd_event_unref(event);
77     }
78 };
79 using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
80 using primarycode_t = uint64_t;
81 using secondarycode_t = std::vector<uint8_t>;
82 using postcode_t = std::tuple<primarycode_t, secondarycode_t>;
83 namespace fs = std::experimental::filesystem;
84 namespace StateServer = sdbusplus::xyz::openbmc_project::State::server;
85 
86 using post_code =
87     sdbusplus::xyz::openbmc_project::State::Boot::server::PostCode;
88 using delete_all =
89     sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll;
90 
91 struct PostCode : sdbusplus::server::object_t<post_code, delete_all>
92 {
93     PostCodeDataHolder *postcodeDataHolderObj =
94         postcodeDataHolderObj->getInstance();
95 
96     PostCode(sdbusplus::bus::bus &bus, const char *path, EventPtr &event) :
97         sdbusplus::server::object_t<post_code, delete_all>(bus, path), bus(bus),
98         propertiesChangedSignalRaw(
99             bus,
100             sdbusplus::bus::match::rules::type::signal() +
101                 sdbusplus::bus::match::rules::member("PropertiesChanged") +
102                 sdbusplus::bus::match::rules::path(
103                     postcodeDataHolderObj->PostCodePath +
104                     std::to_string(postcodeDataHolderObj->node)) +
105                 sdbusplus::bus::match::rules::interface(
106                     postcodeDataHolderObj->PropertiesIntf),
107             [this](sdbusplus::message::message &msg) {
108                 std::string objectName;
109                 std::map<std::string, std::variant<postcode_t>> msgData;
110                 msg.read(objectName, msgData);
111                 // Check if it was the Value property that changed.
112                 auto valPropMap = msgData.find("Value");
113                 {
114                     if (valPropMap != msgData.end())
115                     {
116                         this->savePostCodes(
117                             std::get<postcode_t>(valPropMap->second));
118                     }
119                 }
120             }),
121         propertiesChangedSignalCurrentHostState(
122             bus,
123             sdbusplus::bus::match::rules::type::signal() +
124                 sdbusplus::bus::match::rules::member("PropertiesChanged") +
125                 sdbusplus::bus::match::rules::path(
126                     postcodeDataHolderObj->HostStatePathPrefix +
127                     std::to_string(postcodeDataHolderObj->node)) +
128                 sdbusplus::bus::match::rules::interface(
129                     postcodeDataHolderObj->PropertiesIntf),
130             [this](sdbusplus::message::message &msg) {
131                 std::string objectName;
132                 std::map<std::string, std::variant<std::string>> msgData;
133                 msg.read(objectName, msgData);
134                 // Check if it was the Value property that changed.
135                 auto valPropMap = msgData.find("CurrentHostState");
136                 {
137                     if (valPropMap != msgData.end())
138                     {
139                         StateServer::Host::HostState currentHostState =
140                             StateServer::Host::convertHostStateFromString(
141                                 std::get<std::string>(valPropMap->second));
142                         if (currentHostState ==
143                             StateServer::Host::HostState::Off)
144                         {
145                             if (this->postCodes.empty())
146                             {
147                                 std::cerr << "HostState changed to OFF. Empty "
148                                              "postcode log, keep boot cycle at "
149                                           << this->currentBootCycleIndex
150                                           << std::endl;
151                             }
152                             else
153                             {
154                                 this->postCodes.clear();
155                             }
156                         }
157                     }
158                 }
159             })
160     {
161         phosphor::logging::log<phosphor::logging::level::INFO>(
162             "PostCode is created");
163         auto dir = fs::path(postcodeDataHolderObj->PostCodeListPathPrefix +
164                             std::to_string(postcodeDataHolderObj->node));
165         fs::create_directories(dir);
166         strPostCodeListPath = postcodeDataHolderObj->PostCodeListPathPrefix +
167                               std::to_string(postcodeDataHolderObj->node) + "/";
168         strCurrentBootCycleIndexName = CurrentBootCycleIndexName;
169         uint16_t index = 0;
170         deserialize(
171             fs::path(strPostCodeListPath + strCurrentBootCycleIndexName),
172             index);
173         currentBootCycleIndex = index;
174         strCurrentBootCycleCountName = CurrentBootCycleCountName;
175         uint16_t count = 0;
176         deserialize(
177             fs::path(strPostCodeListPath + strCurrentBootCycleCountName),
178             count);
179         currentBootCycleCount(count);
180         maxBootCycleNum(MaxPostCodeCycles);
181     }
182     ~PostCode()
183     {
184     }
185 
186     std::vector<postcode_t> getPostCodes(uint16_t index) override;
187     std::map<uint64_t, postcode_t>
188         getPostCodesWithTimeStamp(uint16_t index) override;
189     void deleteAll() override;
190 
191   private:
192     void incrBootCycle();
193     uint16_t getBootNum(const uint16_t index) const;
194 
195     sdbusplus::bus::bus &bus;
196     std::chrono::time_point<std::chrono::steady_clock> firstPostCodeTimeSteady;
197     uint64_t firstPostCodeUsSinceEpoch;
198     std::map<uint64_t, postcode_t> postCodes;
199     std::string strPostCodeListPath;
200     std::string strCurrentBootCycleIndexName;
201     uint16_t currentBootCycleIndex;
202     std::string strCurrentBootCycleCountName;
203     void savePostCodes(postcode_t code);
204     sdbusplus::bus::match_t propertiesChangedSignalRaw;
205     sdbusplus::bus::match_t propertiesChangedSignalCurrentHostState;
206     fs::path serialize(const std::string &path);
207     bool deserialize(const fs::path &path, uint16_t &index);
208     bool deserializePostCodes(const fs::path &path,
209                               std::map<uint64_t, postcode_t> &codes);
210 };
211