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