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