1 /** 2 * Copyright © 2019 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 #include "user_header.hpp" 17 18 #include "json_utils.hpp" 19 #include "pel_types.hpp" 20 #include "pel_values.hpp" 21 #include "severity.hpp" 22 23 #include <fmt/format.h> 24 25 #include <phosphor-logging/log.hpp> 26 27 #include <iostream> 28 29 namespace openpower 30 { 31 namespace pels 32 { 33 34 namespace pv = openpower::pels::pel_values; 35 using namespace phosphor::logging; 36 37 void UserHeader::unflatten(Stream& stream) 38 { 39 stream >> _header >> _eventSubsystem >> _eventScope >> _eventSeverity >> 40 _eventType >> _reserved4Byte1 >> _problemDomain >> _problemVector >> 41 _actionFlags >> _states; 42 } 43 44 void UserHeader::flatten(Stream& stream) const 45 { 46 stream << _header << _eventSubsystem << _eventScope << _eventSeverity 47 << _eventType << _reserved4Byte1 << _problemDomain << _problemVector 48 << _actionFlags << _states; 49 } 50 51 UserHeader::UserHeader(const message::Entry& entry, 52 phosphor::logging::Entry::Level severity, 53 const AdditionalData& additionalData, 54 const DataInterfaceBase& dataIface) 55 { 56 _header.id = static_cast<uint16_t>(SectionID::userHeader); 57 _header.size = UserHeader::flattenedSize(); 58 _header.version = userHeaderVersion; 59 _header.subType = 0; 60 _header.componentID = static_cast<uint16_t>(ComponentID::phosphorLogging); 61 62 std::optional<uint8_t> subsys; 63 64 // Check for additional data - PEL_SUBSYSTEM 65 auto ss = additionalData.getValue("PEL_SUBSYSTEM"); 66 if (ss) 67 { 68 auto eventSubsystem = std::stoul(*ss, NULL, 16); 69 std::string subsystemString = pv::getValue(eventSubsystem, 70 pel_values::subsystemValues); 71 if (subsystemString == "invalid") 72 { 73 log<level::WARNING>( 74 fmt::format( 75 "UH: Invalid SubSystem value in PEL_SUBSYSTEM: {:#X}", 76 eventSubsystem) 77 .c_str()); 78 } 79 else 80 { 81 subsys = eventSubsystem; 82 } 83 } 84 else 85 { 86 subsys = entry.subsystem; 87 } 88 89 if (subsys) 90 { 91 _eventSubsystem = *subsys; 92 } 93 else 94 { 95 // Gotta use something, how about 'others'. 96 log<level::WARNING>( 97 "No PEL subystem value supplied for error, using 'others'"); 98 _eventSubsystem = 0x70; 99 } 100 101 _eventScope = entry.eventScope.value_or( 102 static_cast<uint8_t>(EventScope::entirePlatform)); 103 104 { 105 bool mfgSevStatus = false; 106 bool mfgActionFlagStatus = false; 107 108 // Get the mfg severity & action flags 109 if (entry.mfgSeverity || entry.mfgActionFlags) 110 { 111 std::optional<uint8_t> sev = std::nullopt; 112 uint16_t val = 0; 113 114 if (entry.mfgSeverity) 115 { 116 // Find the mf severity possibly dependent on the system type. 117 sev = getSeverity(entry.mfgSeverity.value(), dataIface); 118 } 119 120 if (entry.mfgActionFlags) 121 { 122 // Find the mfg action flags 123 val = entry.mfgActionFlags.value(); 124 } 125 126 if (sev || val) 127 { 128 bool mfgProp = dataIface.getQuiesceOnError(); 129 if (mfgProp) 130 { 131 if (sev) 132 { 133 _eventSeverity = *sev; 134 mfgSevStatus = true; 135 } 136 137 if (val) 138 { 139 _actionFlags = val; 140 mfgActionFlagStatus = true; 141 } 142 } 143 } 144 } 145 146 if (!mfgSevStatus) 147 { 148 // Get the severity from the registry if it's there, otherwise get 149 // it from the OpenBMC event log severity value. 150 if (!entry.severity) 151 { 152 _eventSeverity = convertOBMCSeverityToPEL(severity); 153 } 154 else 155 { 156 // Find the severity possibly dependent on the system type. 157 auto sev = getSeverity(entry.severity.value(), dataIface); 158 if (sev) 159 { 160 _eventSeverity = *sev; 161 } 162 else 163 { 164 // Either someone screwed up the message registry 165 // or getSystemNames failed. 166 log<level::ERR>( 167 "Failed finding the severity in the message registry", 168 phosphor::logging::entry("ERROR=%s", 169 entry.name.c_str())); 170 171 // Have to choose something, just use informational. 172 _eventSeverity = 0; 173 } 174 } 175 } 176 177 // Convert Critical error (0x50) to Critical Error-System Termination 178 // (0x51), if the AdditionalData is set to SYSTEM_TERM 179 auto sevLevel = additionalData.getValue("SEVERITY_DETAIL"); 180 if ((_eventSeverity & 0xF0) == 0x50) 181 { 182 if (sevLevel.value_or("") == "SYSTEM_TERM") 183 { 184 // Change to Critical Error, System Termination 185 _eventSeverity = 0x51; 186 } 187 } 188 189 if (entry.eventType) 190 { 191 _eventType = *entry.eventType; 192 } 193 else 194 { 195 // There are different default event types for info errors 196 // vs non info ones. 197 auto sevType = static_cast<SeverityType>(_eventSeverity & 0xF0); 198 _eventType = 199 (sevType == SeverityType::nonError) 200 ? static_cast<uint8_t>(EventType::miscInformational) 201 : static_cast<uint8_t>(EventType::notApplicable); 202 } 203 204 _reserved4Byte1 = 0; 205 206 // No uses for problem domain or vector 207 _problemDomain = 0; 208 _problemVector = 0; 209 210 // These will be set in pel_rules::check() if they're still 211 // at the default value. 212 if (!mfgActionFlagStatus) 213 { 214 _actionFlags = entry.actionFlags.value_or(actionFlagsDefault); 215 } 216 217 _states = 0; 218 219 _valid = true; 220 } 221 } 222 223 UserHeader::UserHeader(Stream& pel) 224 { 225 try 226 { 227 unflatten(pel); 228 validate(); 229 } 230 catch (const std::exception& e) 231 { 232 log<level::ERR>( 233 fmt::format("Cannot unflatten user header: {}", e.what()).c_str()); 234 _valid = false; 235 } 236 } 237 238 void UserHeader::validate() 239 { 240 bool failed = false; 241 if (header().id != static_cast<uint16_t>(SectionID::userHeader)) 242 { 243 log<level::ERR>( 244 fmt::format("Invalid user header section ID: {0:#x}", header().id) 245 .c_str()); 246 failed = true; 247 } 248 249 if (header().version != userHeaderVersion) 250 { 251 log<level::ERR>( 252 fmt::format("Invalid user header version: {0:#x}", header().version) 253 .c_str()); 254 failed = true; 255 } 256 257 _valid = (failed) ? false : true; 258 } 259 260 std::optional<std::string> UserHeader::getJSON() const 261 { 262 std::string severity; 263 std::string subsystem; 264 std::string eventScope; 265 std::string eventType; 266 std::vector<std::string> actionFlags; 267 severity = pv::getValue(_eventSeverity, pel_values::severityValues); 268 subsystem = pv::getValue(_eventSubsystem, pel_values::subsystemValues); 269 eventScope = pv::getValue(_eventScope, pel_values::eventScopeValues); 270 eventType = pv::getValue(_eventType, pel_values::eventTypeValues); 271 actionFlags = pv::getValuesBitwise(_actionFlags, 272 pel_values::actionFlagsValues); 273 274 std::string hostState{"Invalid"}; 275 std::string hmcState{"Invalid"}; 276 auto iter = pv::transmissionStates.find( 277 static_cast<TransmissionState>(hostTransmissionState())); 278 if (iter != pv::transmissionStates.end()) 279 { 280 hostState = iter->second; 281 } 282 auto iter1 = pv::transmissionStates.find( 283 static_cast<TransmissionState>(hmcTransmissionState())); 284 if (iter1 != pv::transmissionStates.end()) 285 { 286 hmcState = iter1->second; 287 } 288 289 std::string uh; 290 jsonInsert(uh, pv::sectionVer, getNumberString("%d", userHeaderVersion), 1); 291 jsonInsert(uh, pv::subSection, getNumberString("%d", _header.subType), 1); 292 jsonInsert(uh, "Log Committed by", 293 getNumberString("0x%X", _header.componentID), 1); 294 jsonInsert(uh, "Subsystem", subsystem, 1); 295 jsonInsert(uh, "Event Scope", eventScope, 1); 296 jsonInsert(uh, "Event Severity", severity, 1); 297 jsonInsert(uh, "Event Type", eventType, 1); 298 jsonInsertArray(uh, "Action Flags", actionFlags, 1); 299 jsonInsert(uh, "Host Transmission", hostState, 1); 300 jsonInsert(uh, "HMC Transmission", hmcState, 1); 301 uh.erase(uh.size() - 2); 302 return uh; 303 } 304 305 std::optional<uint8_t> UserHeader::getSeverity( 306 const std::vector<message::RegistrySeverity>& severities, 307 const DataInterfaceBase& dataIface) const 308 { 309 const uint8_t* s = nullptr; 310 std::vector<std::string> systemNames; 311 312 // getSystemNames makes D-Bus calls, so only call it if we 313 // know we'll need it because there is a system name in the sev list 314 if (std::any_of(severities.begin(), severities.end(), 315 [](const auto& sev) { return !sev.system.empty(); })) 316 { 317 try 318 { 319 systemNames = dataIface.getSystemNames(); 320 } 321 catch (const std::exception& e) 322 { 323 log<level::ERR>("Failed trying to look up system names on D-Bus", 324 entry("ERROR=%s", e.what())); 325 return std::nullopt; 326 } 327 } 328 329 // Find the severity to use for this system type, or use the default 330 // entry (where no system type is specified). 331 for (const auto& sev : severities) 332 { 333 if (std::find(systemNames.begin(), systemNames.end(), sev.system) != 334 systemNames.end()) 335 { 336 s = &sev.severity; 337 break; 338 } 339 else if (sev.system.empty()) 340 { 341 s = &sev.severity; 342 } 343 } 344 345 if (s) 346 { 347 return *s; 348 } 349 350 return std::nullopt; 351 } 352 353 } // namespace pels 354 } // namespace openpower 355