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