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