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 <phosphor-logging/elog-errors.hpp>
28 #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp>
29 #include <xyz/openbmc_project/Common/error.hpp>
30 #include <xyz/openbmc_project/State/Boot/PostCode/server.hpp>
31 #include <xyz/openbmc_project/State/Host/server.hpp>
32 
33 #include <chrono>
34 #include <filesystem>
35 #include <fstream>
36 #include <iostream>
37 
38 const static constexpr char* CurrentBootCycleCountName =
39     "CurrentBootCycleCount";
40 const static constexpr char* CurrentBootCycleIndexName =
41     "CurrentBootCycleIndex";
42 
43 const static constexpr char* PostCodePath =
44     "/xyz/openbmc_project/state/boot/raw";
45 const static constexpr char* PostCodeListPathPrefix =
46     "/var/lib/phosphor-post-code-manager/host";
47 const static constexpr char* HostStatePathPrefix =
48     "/xyz/openbmc_project/state/host";
49 
50 struct EventDeleter
51 {
52     void operator()(sd_event* event) const
53     {
54         sd_event_unref(event);
55     }
56 };
57 using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
58 using primarycode_t = uint64_t;
59 using secondarycode_t = std::vector<uint8_t>;
60 using postcode_t = std::tuple<primarycode_t, secondarycode_t>;
61 namespace fs = std::filesystem;
62 namespace StateServer = sdbusplus::xyz::openbmc_project::State::server;
63 
64 using post_code =
65     sdbusplus::xyz::openbmc_project::State::Boot::server::PostCode;
66 using delete_all =
67     sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll;
68 
69 struct PostCode : sdbusplus::server::object_t<post_code, delete_all>
70 {
71     PostCode(sdbusplus::bus_t& bus, const char* path, int nodeIndex) :
72         sdbusplus::server::object_t<post_code, delete_all>(bus, path), bus(bus),
73         node(nodeIndex),
74         postCodeListPath(PostCodeListPathPrefix + std::to_string(node)),
75         propertiesChangedSignalRaw(
76             bus,
77             sdbusplus::bus::match::rules::propertiesChanged(
78                 PostCodePath + std::to_string(node),
79                 "xyz.openbmc_project.State.Boot.Raw"),
80             [this](sdbusplus::message_t& msg) {
81                 std::string intfName;
82                 std::map<std::string, std::variant<postcode_t>> msgData;
83                 msg.read(intfName, msgData);
84                 // Check if it was the Value property that changed.
85                 auto valPropMap = msgData.find("Value");
86                 if (valPropMap != msgData.end())
87                 {
88                     this->savePostCodes(
89                         std::get<postcode_t>(valPropMap->second));
90                 }
91             }),
92         propertiesChangedSignalCurrentHostState(
93             bus,
94             sdbusplus::bus::match::rules::propertiesChanged(
95                 HostStatePathPrefix + std::to_string(node),
96                 "xyz.openbmc_project.State.Host"),
97             [this](sdbusplus::message_t& msg) {
98                 std::string intfName;
99                 std::map<std::string, std::variant<std::string>> msgData;
100                 msg.read(intfName, msgData);
101                 // Check if it was the Value property that changed.
102                 auto valPropMap = msgData.find("CurrentHostState");
103                 if (valPropMap != msgData.end())
104                 {
105                     StateServer::Host::HostState currentHostState =
106                         StateServer::Host::convertHostStateFromString(
107                             std::get<std::string>(valPropMap->second));
108                     if (currentHostState == StateServer::Host::HostState::Off)
109                     {
110                         if (this->postCodes.empty())
111                         {
112                             std::cerr << "HostState changed to OFF. Empty "
113                                          "postcode log, keep boot cycle at "
114                                       << this->currentBootCycleIndex
115                                       << std::endl;
116                         }
117                         else
118                         {
119                             this->postCodes.clear();
120                         }
121                     }
122                 }
123             })
124     {
125         phosphor::logging::log<phosphor::logging::level::INFO>(
126             "PostCode is created");
127         fs::create_directories(postCodeListPath);
128         deserialize(postCodeListPath / CurrentBootCycleIndexName,
129                     currentBootCycleIndex);
130         uint16_t count = 0;
131         deserialize(postCodeListPath / CurrentBootCycleCountName, count);
132         currentBootCycleCount(count);
133         maxBootCycleNum(MAX_BOOT_CYCLE_COUNT);
134     }
135     ~PostCode() {}
136 
137     std::vector<postcode_t> getPostCodes(uint16_t index) override;
138     std::map<uint64_t, postcode_t>
139         getPostCodesWithTimeStamp(uint16_t index) override;
140     void deleteAll() override;
141 
142   private:
143     void incrBootCycle();
144     uint16_t getBootNum(const uint16_t index) const;
145 
146     sdbusplus::bus_t& bus;
147     int node;
148     std::chrono::time_point<std::chrono::steady_clock> firstPostCodeTimeSteady;
149     uint64_t firstPostCodeUsSinceEpoch;
150     std::map<uint64_t, postcode_t> postCodes;
151     fs::path postCodeListPath;
152     uint16_t currentBootCycleIndex = 0;
153     sdbusplus::bus::match_t propertiesChangedSignalRaw;
154     sdbusplus::bus::match_t propertiesChangedSignalCurrentHostState;
155 
156     void savePostCodes(postcode_t code);
157     fs::path serialize(const fs::path& path);
158     bool deserialize(const fs::path& path, uint16_t& index);
159     bool deserializePostCodes(const fs::path& path,
160                               std::map<uint64_t, postcode_t>& codes);
161 };
162