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 "extended_user_header.hpp" 17 18 #include "json_utils.hpp" 19 #include "pel_types.hpp" 20 #include "pel_values.hpp" 21 22 #include <fmt/format.h> 23 24 #include <phosphor-logging/log.hpp> 25 26 namespace openpower 27 { 28 namespace pels 29 { 30 31 namespace pv = openpower::pels::pel_values; 32 using namespace phosphor::logging; 33 const size_t defaultSymptomIDWord = 3; 34 const size_t symptomIDMaxSize = 80; 35 36 ExtendedUserHeader::ExtendedUserHeader(Stream& pel) 37 { 38 try 39 { 40 unflatten(pel); 41 validate(); 42 } 43 catch (const std::exception& e) 44 { 45 log<level::ERR>( 46 fmt::format("Cannot unflatten extended user header: {}", e.what()) 47 .c_str()); 48 _valid = false; 49 } 50 } 51 52 ExtendedUserHeader::ExtendedUserHeader(const DataInterfaceBase& dataIface, 53 const message::Entry& regEntry, 54 const SRC& src) : 55 _mtms(dataIface.getMachineTypeModel(), dataIface.getMachineSerialNumber()) 56 { 57 _header.id = static_cast<uint16_t>(SectionID::extendedUserHeader); 58 _header.version = extendedUserHeaderVersion; 59 _header.subType = 0; 60 _header.componentID = static_cast<uint16_t>(ComponentID::phosphorLogging); 61 62 memset(_serverFWVersion.data(), 0, _serverFWVersion.size()); 63 auto version = dataIface.getServerFWVersion(); 64 65 // The last byte must always be the NULL terminator 66 for (size_t i = 0; i < version.size() && i < firmwareVersionSize - 1; i++) 67 { 68 _serverFWVersion[i] = version[i]; 69 } 70 71 memset(_subsystemFWVersion.data(), 0, _subsystemFWVersion.size()); 72 version = dataIface.getBMCFWVersion(); 73 74 // The last byte must always be the NULL terminator 75 for (size_t i = 0; i < version.size() && i < firmwareVersionSize - 1; i++) 76 { 77 _subsystemFWVersion[i] = version[i]; 78 } 79 80 createSymptomID(regEntry, src); 81 82 _header.size = flattenedSize(); 83 _valid = true; 84 } 85 86 void ExtendedUserHeader::flatten(Stream& pel) const 87 { 88 pel << _header << _mtms; 89 pel.write(_serverFWVersion.data(), _serverFWVersion.size()); 90 pel.write(_subsystemFWVersion.data(), _subsystemFWVersion.size()); 91 pel << _reserved4B << _refTime << _reserved1B1 << _reserved1B2 92 << _reserved1B3 << _symptomIDSize << _symptomID; 93 } 94 95 void ExtendedUserHeader::unflatten(Stream& pel) 96 { 97 pel >> _header >> _mtms; 98 pel.read(_serverFWVersion.data(), _serverFWVersion.size()); 99 pel.read(_subsystemFWVersion.data(), _subsystemFWVersion.size()); 100 pel >> _reserved4B >> _refTime >> _reserved1B1 >> _reserved1B2 >> 101 _reserved1B3 >> _symptomIDSize; 102 103 _symptomID.resize(_symptomIDSize); 104 pel >> _symptomID; 105 } 106 107 void ExtendedUserHeader::validate() 108 { 109 bool failed = false; 110 111 if (header().id != static_cast<uint16_t>(SectionID::extendedUserHeader)) 112 { 113 log<level::ERR>( 114 fmt::format("Invalid ExtendedUserHeader section ID: {0:#x}", 115 header().id) 116 .c_str()); 117 failed = true; 118 } 119 120 if (header().version != extendedUserHeaderVersion) 121 { 122 log<level::ERR>( 123 fmt::format("Invalid ExtendedUserHeader version: {0:#x}", 124 header().version) 125 .c_str()); 126 failed = true; 127 } 128 129 _valid = (failed) ? false : true; 130 } 131 132 void ExtendedUserHeader::createSymptomID(const message::Entry& regEntry, 133 const SRC& src) 134 { 135 // Contains the first 8 characters of the ASCII string plus additional 136 // words from the SRC, separated by underscores. The message registry 137 // says which words to use, though that's optional and if not present 138 // then use a default word. 139 std::vector<size_t> idWords; 140 141 if (regEntry.src.symptomID) 142 { 143 idWords = regEntry.src.symptomID.value(); 144 } 145 else 146 { 147 idWords.push_back(defaultSymptomIDWord); 148 } 149 150 auto symptomID = src.asciiString().substr(0, 8); 151 152 const auto& hexWords = src.hexwordData(); 153 154 for (auto wordNum : idWords) 155 { 156 symptomID.push_back('_'); 157 158 // Get the hexword array index for this SRC word 159 auto index = src.getWordIndexFromWordNum(wordNum); 160 161 // Convert to ASCII 162 char word[20]; 163 sprintf(word, "%08X", hexWords[index]); 164 symptomID += word; 165 } 166 167 std::copy(symptomID.begin(), symptomID.end(), 168 std::back_inserter(_symptomID)); 169 170 // Max total size is 80, including the upcoming NULL 171 if (_symptomID.size() > (symptomIDMaxSize - 1)) 172 { 173 _symptomID.resize(symptomIDMaxSize - 1); 174 } 175 176 // NULL terminated 177 _symptomID.push_back(0); 178 179 // PAD with NULLs to a 4 byte boundary 180 while ((_symptomID.size() % 4) != 0) 181 { 182 _symptomID.push_back(0); 183 } 184 185 _symptomIDSize = _symptomID.size(); 186 } 187 188 std::optional<std::string> ExtendedUserHeader::getJSON() const 189 { 190 std::string json; 191 jsonInsert(json, pv::sectionVer, getNumberString("%d", _header.version), 1); 192 jsonInsert(json, pv::subSection, getNumberString("%d", _header.subType), 1); 193 jsonInsert(json, pv::createdBy, 194 getNumberString("0x%X", _header.componentID), 1); 195 jsonInsert(json, "Reporting Machine Type", machineTypeModel(), 1); 196 jsonInsert(json, "Reporting Serial Number", trimEnd(machineSerialNumber()), 197 1); 198 jsonInsert(json, "FW Released Ver", serverFWVersion(), 1); 199 jsonInsert(json, "FW SubSys Version", subsystemFWVersion(), 1); 200 jsonInsert(json, "Common Ref Time", 201 getNumberString("%02X", _refTime.month) + '/' + 202 getNumberString("%02X", _refTime.day) + '/' + 203 getNumberString("%02X", _refTime.yearMSB) + 204 getNumberString("%02X", _refTime.yearLSB) + ' ' + 205 getNumberString("%02X", _refTime.hour) + ':' + 206 getNumberString("%02X", _refTime.minutes) + ':' + 207 getNumberString("%02X", _refTime.seconds), 208 1); 209 jsonInsert(json, "Symptom Id Len", getNumberString("%d", _symptomIDSize), 210 1); 211 jsonInsert(json, "Symptom Id", symptomID(), 1); 212 json.erase(json.size() - 2); 213 return json; 214 } 215 216 } // namespace pels 217 } // namespace openpower 218