1 /**
2  * Copyright © 2022 IBM 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 
17 #include "extensions/phal/clock_logger.hpp"
18 
19 #include "util.hpp"
20 
21 #include <attributes_info.H>
22 #include <libphal.H>
23 
24 #include <phosphor-logging/lg2.hpp>
25 #include <sdeventplus/event.hpp>
26 #include <sdeventplus/utility/timer.hpp>
27 
28 #include <chrono>
29 
30 using namespace openpower::pel;
31 
32 PHOSPHOR_LOG2_USING;
33 
34 namespace openpower::phal::clock
35 {
36 constexpr auto CLOCK_DAILY_LOGGER_TIMEOUT_IN_HOUR = 24;
37 
38 Manager::Manager(const sdeventplus::Event& event) :
39     _event(event), timer(event, std::bind(&Manager::timerExpired, this))
40 
41 {
42     try
43     {
44         // pdbg initialisation
45         openpower::phal::pdbg::init();
46 
47         // Create clock data log.
48         createClockDataLog();
49     }
50     catch (const std::exception& e)
51     {
52         error("Clock Data Log exception ({ERROR})", "ERROR", e);
53     }
54 
55     addTimer();
56 }
57 
58 void Manager::addTimer()
59 {
60     // Set timer for 24 hours.
61     timer.restart(std::chrono::hours(CLOCK_DAILY_LOGGER_TIMEOUT_IN_HOUR));
62 }
63 
64 void Manager::timerExpired()
65 {
66     info("Clock daily logging started");
67 
68     try
69     {
70         // Create clock data log.
71         createClockDataLog();
72     }
73     catch (const std::exception& e)
74     {
75         error("createClockDataLog exception ({ERROR})", "ERROR", e);
76     }
77 }
78 
79 void Manager::createClockDataLog()
80 {
81     // check chassis power state.
82     auto powerState = openpower::util::getChassisPowerState();
83 
84     if (powerState != "xyz.openbmc_project.State.Chassis.PowerState.On")
85     {
86         warning("The chassis power state({POWERSTATE}) is not ON, Skipping "
87                 "clock data "
88                 "logging",
89                 "POWERSTATE", powerState);
90         return;
91     }
92 
93     // Data logger storage
94     FFDCData clockDataLog;
95 
96     struct pdbg_target* procTarget;
97     ATTR_HWAS_STATE_Type hwasState;
98     pdbg_for_each_class_target("proc", procTarget)
99     {
100         if (DT_GET_PROP(ATTR_HWAS_STATE, procTarget, hwasState))
101         {
102             error("{TARGET} Could not read HWAS_STATE attribute", "TARGET",
103                   pdbg_target_path(procTarget));
104             continue;
105         }
106         if (!hwasState.present)
107         {
108             continue;
109         }
110 
111         auto index = std::to_string(pdbg_target_index(procTarget));
112 
113         // update functional State
114         std::string funState = "Non Functional";
115 
116         if (hwasState.functional)
117         {
118             funState = "Functional";
119         }
120         std::stringstream ssState;
121         ssState << "Proc" << index;
122         clockDataLog.push_back(std::make_pair(ssState.str(), funState));
123 
124         // update location code information
125         ATTR_LOCATION_CODE_Type locationCode;
126         memset(&locationCode, '\0', sizeof(locationCode));
127         try
128         {
129             openpower::phal::pdbg::getLocationCode(procTarget, locationCode);
130         }
131         catch (const std::exception& e)
132         {
133             error("getLocationCode on {TARGET} thrown exception ({ERROR})",
134                   "TARGET", pdbg_target_path(procTarget), "ERROR", e);
135         }
136         std::stringstream ssLoc;
137         ssLoc << "Proc" << index << " Location Code";
138         clockDataLog.push_back(std::make_pair(ssLoc.str(), locationCode));
139 
140         // Update Processor EC level
141         ATTR_EC_Type ecVal = 0;
142         if (DT_GET_PROP(ATTR_EC, procTarget, ecVal))
143         {
144             error("Could not read ATTR_EC  attribute");
145         }
146         std::stringstream ssEC;
147         ssEC << "Proc" << index << " EC";
148 
149         std::stringstream ssECVal;
150         ssECVal << "0x" << std::setfill('0') << std::setw(10) << std::hex
151                 << (uint16_t)ecVal;
152         clockDataLog.push_back(std::make_pair(ssEC.str(), ssECVal.str()));
153 
154         // Add CFAM register information.
155         addCFAMData(procTarget, clockDataLog);
156     }
157 
158     // Add clock register information
159     addClockRegData(clockDataLog);
160 
161     openpower::pel::createPEL("org.open_power.PHAL.Info.ClockDailyLog",
162                               clockDataLog, Severity::Informational);
163 }
164 
165 void Manager::addCFAMData(struct pdbg_target* proc,
166                           openpower::pel::FFDCData& clockDataLog)
167 {
168     // collect Processor CFAM register data
169     const std::vector<int> procCFAMAddr = {
170         0x1007, 0x2804, 0x2810, 0x2813, 0x2814, 0x2815, 0x2816, 0x281D, 0x281E};
171 
172     auto index = std::to_string(pdbg_target_index(proc));
173 
174     for (int addr : procCFAMAddr)
175     {
176         auto val = 0xDEADBEEF;
177         try
178         {
179             val = openpower::phal::pdbg::getCFAM(proc, addr);
180         }
181         catch (const std::exception& e)
182         {
183             error("getCFAM on {TARGET} thrown exception({ERROR}): Addr ({REG})",
184                   "TARGET", pdbg_target_path(proc), "ERROR", e, "REG", addr);
185         }
186         std::stringstream ssData;
187         ssData << "0x" << std::setfill('0') << std::setw(8) << std::hex << val;
188         std::stringstream ssAddr;
189         ssAddr << "Proc" << index << " REG 0x" << std::hex << addr;
190         // update the data
191         clockDataLog.push_back(make_pair(ssAddr.str(), ssData.str()));
192     }
193 }
194 
195 void Manager::addClockRegData(openpower::pel::FFDCData& clockDataLog)
196 {
197     info("Adding clock register information to daily logger");
198 
199     struct pdbg_target* clockTarget;
200     pdbg_for_each_class_target("oscrefclk", clockTarget)
201     {
202         ATTR_HWAS_STATE_Type hwasState;
203         if (DT_GET_PROP(ATTR_HWAS_STATE, clockTarget, hwasState))
204         {
205             error("({TARGET}) Could not read HWAS_STATE attribute", "TARGET",
206                   pdbg_target_path(clockTarget));
207             continue;
208         }
209 
210         if (!hwasState.present)
211         {
212             continue;
213         }
214 
215         std::string funState = "Non Functional";
216 
217         if (hwasState.functional)
218         {
219             funState = "Functional";
220         }
221 
222         auto index = std::to_string(pdbg_target_index(clockTarget));
223 
224         std::stringstream ssState;
225         ssState << "Clock" << index;
226         clockDataLog.push_back(std::make_pair(ssState.str(), funState));
227 
228         // Add clcok device path information
229         std::stringstream ssName;
230         ssName << "Clock" << index << " path";
231         clockDataLog.push_back(
232             std::make_pair(ssName.str(), pdbg_target_path(clockTarget)));
233 
234         auto status = pdbg_target_probe(clockTarget);
235         if (status != PDBG_TARGET_ENABLED)
236         {
237             continue;
238         }
239 
240         // Update Buffer with clock I2C register data.
241         auto constexpr I2C_READ_SIZE = 0x08;
242         auto constexpr I2C_ADDR_MAX = 0xFF;
243 
244         for (auto addr = 0; addr <= I2C_ADDR_MAX; addr += I2C_READ_SIZE)
245         {
246             std::stringstream ssData;
247 
248             uint8_t data[0x8];
249             auto i2cRc = i2c_read(clockTarget, 0, addr, I2C_READ_SIZE, data);
250             if (i2cRc)
251             {
252                 error("({TARGET}) I2C read error({ERROR}) reported {ADDRESS} ",
253                       "TARGET", pdbg_target_path(clockTarget), "ERROR", i2cRc,
254                       "ADDRESS", addr);
255                 continue;
256             }
257 
258             for (auto i = 0; i < I2C_READ_SIZE; i++)
259             {
260                 ssData << " " << std::hex << std::setfill('0') << std::setw(2)
261                        << (uint16_t)data[i];
262             }
263             std::stringstream ssAddr;
264             ssAddr << "Clock" << index << "_0x" << std::hex << std::setfill('0')
265                    << std::setw(2) << addr;
266             clockDataLog.push_back(make_pair(ssAddr.str(), ssData.str()));
267         }
268     }
269 }
270 
271 } // namespace openpower::phal::clock
272