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 
Manager(const sdeventplus::Event & event)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 
addTimer()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 
timerExpired()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 
createClockDataLog()79 void Manager::createClockDataLog()
80 {
81     // Data logger storage
82     FFDCData clockDataLog;
83 
84     struct pdbg_target* procTarget;
85     ATTR_HWAS_STATE_Type hwasState;
86     pdbg_for_each_class_target("proc", procTarget)
87     {
88         if (DT_GET_PROP(ATTR_HWAS_STATE, procTarget, hwasState))
89         {
90             error("{TARGET} Could not read HWAS_STATE attribute", "TARGET",
91                   pdbg_target_path(procTarget));
92             continue;
93         }
94         if (!hwasState.present)
95         {
96             continue;
97         }
98 
99         auto index = std::to_string(pdbg_target_index(procTarget));
100 
101         // update functional State
102         std::string funState = "Non Functional";
103 
104         if (hwasState.functional)
105         {
106             funState = "Functional";
107         }
108         std::stringstream ssState;
109         ssState << "Proc" << index;
110         clockDataLog.push_back(std::make_pair(ssState.str(), funState));
111 
112         // update location code information
113         ATTR_LOCATION_CODE_Type locationCode;
114         memset(&locationCode, '\0', sizeof(locationCode));
115         try
116         {
117             openpower::phal::pdbg::getLocationCode(procTarget, locationCode);
118         }
119         catch (const std::exception& e)
120         {
121             error("getLocationCode on {TARGET} thrown exception ({ERROR})",
122                   "TARGET", pdbg_target_path(procTarget), "ERROR", e);
123         }
124         std::stringstream ssLoc;
125         ssLoc << "Proc" << index << " Location Code";
126         clockDataLog.push_back(std::make_pair(ssLoc.str(), locationCode));
127 
128         // Update Processor EC level
129         ATTR_EC_Type ecVal = 0;
130         if (DT_GET_PROP(ATTR_EC, procTarget, ecVal))
131         {
132             error("Could not read ATTR_EC  attribute");
133         }
134         std::stringstream ssEC;
135         ssEC << "Proc" << index << " EC";
136 
137         std::stringstream ssECVal;
138         ssECVal << "0x" << std::setfill('0') << std::setw(10) << std::hex
139                 << (uint16_t)ecVal;
140         clockDataLog.push_back(std::make_pair(ssEC.str(), ssECVal.str()));
141 
142         // Add CFAM register information.
143         addCFAMData(procTarget, clockDataLog);
144     }
145 
146     // Add clock register information
147     addClockRegData(clockDataLog);
148 
149     openpower::pel::createPEL("org.open_power.PHAL.Info.ClockDailyLog",
150                               clockDataLog, Severity::Informational);
151 }
152 
addCFAMData(struct pdbg_target * proc,openpower::pel::FFDCData & clockDataLog)153 void Manager::addCFAMData(struct pdbg_target* proc,
154                           openpower::pel::FFDCData& clockDataLog)
155 {
156     // collect Processor CFAM register data
157     const std::vector<int> procCFAMAddr = {
158         0x1007, 0x2804, 0x2810, 0x2813, 0x2814, 0x2815, 0x2816, 0x281D, 0x281E};
159 
160     auto index = std::to_string(pdbg_target_index(proc));
161 
162     for (int addr : procCFAMAddr)
163     {
164         auto val = 0xDEADBEEF;
165         try
166         {
167             val = openpower::phal::pdbg::getCFAM(proc, addr);
168         }
169         catch (const std::exception& e)
170         {
171             error("getCFAM on {TARGET} thrown exception({ERROR}): Addr ({REG})",
172                   "TARGET", pdbg_target_path(proc), "ERROR", e, "REG", addr);
173         }
174         std::stringstream ssData;
175         ssData << "0x" << std::setfill('0') << std::setw(8) << std::hex << val;
176         std::stringstream ssAddr;
177         ssAddr << "Proc" << index << " REG 0x" << std::hex << addr;
178         // update the data
179         clockDataLog.push_back(make_pair(ssAddr.str(), ssData.str()));
180     }
181 }
182 
addClockRegData(openpower::pel::FFDCData & clockDataLog)183 void Manager::addClockRegData(openpower::pel::FFDCData& clockDataLog)
184 {
185     info("Adding clock register information to daily logger");
186 
187     struct pdbg_target* clockTarget;
188     pdbg_for_each_class_target("oscrefclk", clockTarget)
189     {
190         ATTR_HWAS_STATE_Type hwasState;
191         if (DT_GET_PROP(ATTR_HWAS_STATE, clockTarget, hwasState))
192         {
193             error("({TARGET}) Could not read HWAS_STATE attribute", "TARGET",
194                   pdbg_target_path(clockTarget));
195             continue;
196         }
197 
198         if (!hwasState.present)
199         {
200             continue;
201         }
202 
203         std::string funState = "Non Functional";
204 
205         if (hwasState.functional)
206         {
207             funState = "Functional";
208         }
209 
210         auto index = std::to_string(pdbg_target_index(clockTarget));
211 
212         std::stringstream ssState;
213         ssState << "Clock" << index;
214         clockDataLog.push_back(std::make_pair(ssState.str(), funState));
215 
216         // Add clcok device path information
217         std::stringstream ssName;
218         ssName << "Clock" << index << " path";
219         clockDataLog.push_back(
220             std::make_pair(ssName.str(), pdbg_target_path(clockTarget)));
221 
222         auto status = pdbg_target_probe(clockTarget);
223         if (status != PDBG_TARGET_ENABLED)
224         {
225             continue;
226         }
227 
228         // Update Buffer with clock I2C register data.
229         auto constexpr I2C_READ_SIZE = 0x08;
230         auto constexpr I2C_ADDR_MAX = 0xFF;
231 
232         for (auto addr = 0; addr <= I2C_ADDR_MAX; addr += I2C_READ_SIZE)
233         {
234             std::stringstream ssData;
235 
236             uint8_t data[0x8];
237             auto i2cRc = i2c_read(clockTarget, 0, addr, I2C_READ_SIZE, data);
238             if (i2cRc)
239             {
240                 error("({TARGET}) I2C read error({ERROR}) reported {ADDRESS} ",
241                       "TARGET", pdbg_target_path(clockTarget), "ERROR", i2cRc,
242                       "ADDRESS", addr);
243                 continue;
244             }
245 
246             for (auto i = 0; i < I2C_READ_SIZE; i++)
247             {
248                 ssData << " " << std::hex << std::setfill('0') << std::setw(2)
249                        << (uint16_t)data[i];
250             }
251             std::stringstream ssAddr;
252             ssAddr << "Clock" << index << "_0x" << std::hex << std::setfill('0')
253                    << std::setw(2) << addr;
254             clockDataLog.push_back(make_pair(ssAddr.str(), ssData.str()));
255         }
256     }
257 }
258 
259 } // namespace openpower::phal::clock
260