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