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 "fru_identity.hpp"
17
18 #include "pel_values.hpp"
19
20 #include <phosphor-logging/lg2.hpp>
21
22 #include <format>
23
24 namespace openpower
25 {
26 namespace pels
27 {
28 namespace src
29 {
30
31 namespace
32 {
33
34 /**
35 * @brief Fills in the std::array from the string value
36 *
37 * If the string is shorter than the array, it will be padded with
38 * '\0's.
39 *
40 * @param[in] source - The string to fill in the array with
41 * @param[out] target - The input object that supports [] and size()
42 */
43 template <typename T>
fillArray(const std::string & source,T & target)44 void fillArray(const std::string& source, T& target)
45 {
46 for (size_t i = 0; i < target.size(); i++)
47 {
48 target[i] = (source.size() > i) ? source[i] : '\0';
49 }
50 }
51
52 } // namespace
53
FRUIdentity(Stream & pel)54 FRUIdentity::FRUIdentity(Stream& pel)
55 {
56 pel >> _type >> _size >> _flags;
57
58 if (hasPN() || hasMP())
59 {
60 pel.read(_pnOrProcedureID.data(), _pnOrProcedureID.size());
61 }
62
63 if (hasCCIN())
64 {
65 pel.read(_ccin.data(), _ccin.size());
66 }
67
68 if (hasSN())
69 {
70 pel.read(_sn.data(), _sn.size());
71 }
72 }
73
flattenedSize() const74 size_t FRUIdentity::flattenedSize() const
75 {
76 size_t size = sizeof(_type) + sizeof(_size) + sizeof(_flags);
77
78 if (hasPN() || hasMP())
79 {
80 size += _pnOrProcedureID.size();
81 }
82
83 if (hasCCIN())
84 {
85 size += _ccin.size();
86 }
87
88 if (hasSN())
89 {
90 size += _sn.size();
91 }
92
93 return size;
94 }
95
FRUIdentity(const std::string & partNumber,const std::string & ccin,const std::string & serialNumber)96 FRUIdentity::FRUIdentity(const std::string& partNumber, const std::string& ccin,
97 const std::string& serialNumber)
98 {
99 _type = substructureType;
100 _flags = hardwareFRU;
101
102 setPartNumber(partNumber);
103 setCCIN(ccin);
104 setSerialNumber(serialNumber);
105
106 _size = flattenedSize();
107 }
108
FRUIdentity(const std::string & procedure,CalloutValueType type)109 FRUIdentity::FRUIdentity(const std::string& procedure, CalloutValueType type)
110 {
111 _type = substructureType;
112 _flags = maintenanceProc;
113
114 setMaintenanceProcedure(procedure, type);
115
116 _size = flattenedSize();
117 }
118
FRUIdentity(const std::string & fru,CalloutValueType type,bool trustedLocationCode)119 FRUIdentity::FRUIdentity(const std::string& fru, CalloutValueType type,
120 bool trustedLocationCode)
121 {
122 _type = substructureType;
123 _flags = (trustedLocationCode) ? symbolicFRUTrustedLocCode : symbolicFRU;
124
125 setSymbolicFRU(fru, type);
126
127 _size = flattenedSize();
128 }
129
getPN() const130 std::optional<std::string> FRUIdentity::getPN() const
131 {
132 if (hasPN())
133 {
134 // NULL terminated
135 std::string pn{_pnOrProcedureID.data()};
136 return pn;
137 }
138
139 return std::nullopt;
140 }
141
getMaintProc() const142 std::optional<std::string> FRUIdentity::getMaintProc() const
143 {
144 if (hasMP())
145 {
146 // NULL terminated
147 std::string mp{_pnOrProcedureID.data()};
148 return mp;
149 }
150
151 return std::nullopt;
152 }
153
getCCIN() const154 std::optional<std::string> FRUIdentity::getCCIN() const
155 {
156 if (hasCCIN())
157 {
158 std::string ccin{_ccin.begin(), _ccin.begin() + _ccin.size()};
159
160 // Don't leave any NULLs in the string (not there usually)
161 if (auto pos = ccin.find('\0'); pos != std::string::npos)
162 {
163 ccin.resize(pos);
164 }
165 return ccin;
166 }
167
168 return std::nullopt;
169 }
170
getSN() const171 std::optional<std::string> FRUIdentity::getSN() const
172 {
173 if (hasSN())
174 {
175 std::string sn{_sn.begin(), _sn.begin() + _sn.size()};
176
177 // Don't leave any NULLs in the string (not there usually)
178 if (auto pos = sn.find('\0'); pos != std::string::npos)
179 {
180 sn.resize(pos);
181 }
182 return sn;
183 }
184
185 return std::nullopt;
186 }
187
flatten(Stream & pel) const188 void FRUIdentity::flatten(Stream& pel) const
189 {
190 pel << _type << _size << _flags;
191
192 if (hasPN() || hasMP())
193 {
194 pel.write(_pnOrProcedureID.data(), _pnOrProcedureID.size());
195 }
196
197 if (hasCCIN())
198 {
199 pel.write(_ccin.data(), _ccin.size());
200 }
201
202 if (hasSN())
203 {
204 pel.write(_sn.data(), _sn.size());
205 }
206 }
207
setPartNumber(const std::string & partNumber)208 void FRUIdentity::setPartNumber(const std::string& partNumber)
209 {
210 _flags |= pnSupplied;
211 _flags &= ~maintProcSupplied;
212
213 auto pn = partNumber;
214
215 // Strip leading whitespace on this one.
216 while (!pn.empty() && (' ' == pn.front()))
217 {
218 pn.erase(0, 1);
219 }
220
221 fillArray(pn, _pnOrProcedureID);
222
223 // ensure null terminated
224 _pnOrProcedureID.back() = 0;
225 }
226
setCCIN(const std::string & ccin)227 void FRUIdentity::setCCIN(const std::string& ccin)
228 {
229 _flags |= ccinSupplied;
230
231 fillArray(ccin, _ccin);
232 }
233
setSerialNumber(const std::string & serialNumber)234 void FRUIdentity::setSerialNumber(const std::string& serialNumber)
235 {
236 _flags |= snSupplied;
237
238 fillArray(serialNumber, _sn);
239 }
240
setMaintenanceProcedure(const std::string & procedure,CalloutValueType type)241 void FRUIdentity::setMaintenanceProcedure(const std::string& procedure,
242 CalloutValueType type)
243 {
244 _flags |= maintProcSupplied;
245 _flags &= ~pnSupplied;
246
247 if (type == CalloutValueType::registryName)
248 {
249 if (pel_values::maintenanceProcedures.count(procedure))
250 {
251 fillArray(pel_values::maintenanceProcedures.at(procedure),
252 _pnOrProcedureID);
253 }
254 else
255 {
256 lg2::error("Invalid maintenance procedure {PROCEDURE}", "PROCEDURE",
257 procedure);
258 strncpy(_pnOrProcedureID.data(), "INVALID",
259 _pnOrProcedureID.size());
260 }
261 }
262 else
263 {
264 fillArray(procedure, _pnOrProcedureID);
265 }
266
267 // ensure null terminated
268 _pnOrProcedureID.back() = 0;
269 }
270
setSymbolicFRU(const std::string & symbolicFRU,CalloutValueType type)271 void FRUIdentity::setSymbolicFRU(const std::string& symbolicFRU,
272 CalloutValueType type)
273 {
274 // Treat this has a HW callout.
275 _flags |= pnSupplied;
276 _flags &= ~maintProcSupplied;
277
278 if (type == CalloutValueType::registryName)
279 {
280 if (pel_values::symbolicFRUs.count(symbolicFRU))
281 {
282 fillArray(pel_values::symbolicFRUs.at(symbolicFRU),
283 _pnOrProcedureID);
284 }
285 else
286 {
287 lg2::error("Invalid symbolic FRU {FRU}", "FRU", symbolicFRU);
288 strncpy(_pnOrProcedureID.data(), "INVALID",
289 _pnOrProcedureID.size());
290 }
291 }
292 else
293 {
294 fillArray(symbolicFRU, _pnOrProcedureID);
295 }
296
297 // ensure null terminated
298 _pnOrProcedureID.back() = 0;
299 }
300
301 } // namespace src
302 } // namespace pels
303 } // namespace openpower
304