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>
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 
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 
74 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 
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 
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 
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 
130 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 
142 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 
154 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 
171 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 
188 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 
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 
227 void FRUIdentity::setCCIN(const std::string& ccin)
228 {
229     _flags |= ccinSupplied;
230 
231     fillArray(ccin, _ccin);
232 }
233 
234 void FRUIdentity::setSerialNumber(const std::string& serialNumber)
235 {
236     _flags |= snSupplied;
237 
238     fillArray(serialNumber, _sn);
239 }
240 
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 
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