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 <phosphor-logging/log.hpp>
23 
24 #include <format>
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             std::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             std::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             std::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(uint8_t creatorID) 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                getComponentName(_header.componentID, creatorID), 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