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 "callout.hpp"
17 
18 #include <phosphor-logging/lg2.hpp>
19 
20 #include <map>
21 
22 namespace openpower
23 {
24 namespace pels
25 {
26 namespace src
27 {
28 
29 constexpr size_t locationCodeMaxSize = 80;
30 
31 Callout::Callout(Stream& pel)
32 {
33     pel >> _size >> _flags >> _priority >> _locationCodeSize;
34 
35     if (_locationCodeSize)
36     {
37         _locationCode.resize(_locationCodeSize);
38         pel >> _locationCode;
39     }
40 
41     size_t currentSize = 4 + _locationCodeSize;
42 
43     // Read in the substructures until the end of this structure.
44     // Any stream overflows will throw an exception up to the SRC constructor
45     while (_size > currentSize)
46     {
47         // Peek the type
48         uint16_t type = 0;
49         pel >> type;
50         pel.offset(pel.offset() - 2);
51 
52         switch (type)
53         {
54             case FRUIdentity::substructureType:
55             {
56                 _fruIdentity = std::make_unique<FRUIdentity>(pel);
57                 currentSize += _fruIdentity->flattenedSize();
58                 break;
59             }
60             case PCEIdentity::substructureType:
61             {
62                 _pceIdentity = std::make_unique<PCEIdentity>(pel);
63                 currentSize += _pceIdentity->flattenedSize();
64                 break;
65             }
66             case MRU::substructureType:
67             {
68                 _mru = std::make_unique<MRU>(pel);
69                 currentSize += _mru->flattenedSize();
70                 break;
71             }
72             default:
73                 lg2::error("Invalid Callout subsection type {CALLOUT_TYPE}",
74                            "CALLOUT_TYPE", lg2::hex, type);
75                 throw std::runtime_error("Invalid Callout subsection type");
76                 break;
77         }
78     }
79 }
80 
81 Callout::Callout(CalloutPriority priority, const std::string& locationCode,
82                  const std::string& partNumber, const std::string& ccin,
83                  const std::string& serialNumber) :
84     Callout(priority, locationCode, partNumber, ccin, serialNumber,
85             std::vector<MRU::MRUCallout>{})
86 {}
87 
88 Callout::Callout(CalloutPriority priority, const std::string& locationCode,
89                  const std::string& partNumber, const std::string& ccin,
90                  const std::string& serialNumber,
91                  const std::vector<MRU::MRUCallout>& mrus)
92 {
93     _flags = calloutType | fruIdentIncluded;
94 
95     _priority = static_cast<uint8_t>(priority);
96 
97     setLocationCode(locationCode);
98 
99     _fruIdentity =
100         std::make_unique<FRUIdentity>(partNumber, ccin, serialNumber);
101 
102     if (!mrus.empty())
103     {
104         _flags |= mruIncluded;
105         _mru = std::make_unique<MRU>(mrus);
106     }
107 
108     _size = flattenedSize();
109 }
110 
111 Callout::Callout(CalloutPriority priority, const std::string& procedure,
112                  CalloutValueType type)
113 {
114     _flags = calloutType | fruIdentIncluded;
115 
116     _priority = static_cast<uint8_t>(priority);
117 
118     _locationCodeSize = 0;
119 
120     _fruIdentity = std::make_unique<FRUIdentity>(procedure, type);
121 
122     _size = flattenedSize();
123 }
124 
125 Callout::Callout(CalloutPriority priority, const std::string& symbolicFRU,
126                  CalloutValueType type, const std::string& locationCode,
127                  bool trustedLocationCode)
128 {
129     _flags = calloutType | fruIdentIncluded;
130 
131     _priority = static_cast<uint8_t>(priority);
132 
133     setLocationCode(locationCode);
134 
135     _fruIdentity =
136         std::make_unique<FRUIdentity>(symbolicFRU, type, trustedLocationCode);
137 
138     _size = flattenedSize();
139 }
140 
141 void Callout::setLocationCode(const std::string& locationCode)
142 {
143     if (locationCode.empty())
144     {
145         _locationCodeSize = 0;
146         return;
147     }
148 
149     std::copy(locationCode.begin(), locationCode.end(),
150               std::back_inserter(_locationCode));
151 
152     if (_locationCode.size() < locationCodeMaxSize)
153     {
154         // Add a NULL, and then pad to a 4B boundary
155         _locationCode.push_back('\0');
156 
157         while (_locationCode.size() % 4)
158         {
159             _locationCode.push_back('\0');
160         }
161     }
162     else
163     {
164         // Too big - truncate it and ensure it ends in a NULL.
165         _locationCode.resize(locationCodeMaxSize);
166         _locationCode.back() = '\0';
167     }
168 
169     _locationCodeSize = _locationCode.size();
170 }
171 
172 size_t Callout::flattenedSize() const
173 {
174     size_t size = sizeof(_size) + sizeof(_flags) + sizeof(_priority) +
175                   sizeof(_locationCodeSize) + _locationCodeSize;
176 
177     size += _fruIdentity ? _fruIdentity->flattenedSize() : 0;
178     size += _pceIdentity ? _pceIdentity->flattenedSize() : 0;
179     size += _mru ? _mru->flattenedSize() : 0;
180 
181     return size;
182 }
183 
184 void Callout::flatten(Stream& pel) const
185 {
186     pel << _size << _flags << _priority << _locationCodeSize;
187 
188     if (_locationCodeSize)
189     {
190         pel << _locationCode;
191     }
192 
193     if (_fruIdentity)
194     {
195         _fruIdentity->flatten(pel);
196     }
197 
198     if (_pceIdentity)
199     {
200         _pceIdentity->flatten(pel);
201     }
202     if (_mru)
203     {
204         _mru->flatten(pel);
205     }
206 }
207 
208 bool Callout::operator==(const Callout& right) const
209 {
210     if ((_locationCodeSize != 0) || (right.locationCodeSize() != 0))
211     {
212         return locationCode() == right.locationCode();
213     }
214 
215     if (!_fruIdentity || !right.fruIdentity())
216     {
217         return false;
218     }
219 
220     auto myProc = _fruIdentity->getMaintProc();
221     auto otherProc = right.fruIdentity()->getMaintProc();
222     if (myProc)
223     {
224         if (otherProc)
225         {
226             return *myProc == *otherProc;
227         }
228         return false;
229     }
230 
231     auto myPN = _fruIdentity->getPN();
232     auto otherPN = right.fruIdentity()->getPN();
233     if (myPN && otherPN)
234     {
235         return *myPN == *otherPN;
236     }
237 
238     return false;
239 }
240 
241 bool Callout::operator>(const Callout& right) const
242 {
243     // Treat all of the mediums the same
244     const std::map<std::uint8_t, int> priorities = {
245         {'H', 10}, {'M', 9}, {'A', 9}, {'B', 9}, {'C', 9}, {'L', 8}};
246 
247     if (!priorities.contains(priority()) ||
248         !priorities.contains(right.priority()))
249     {
250         return false;
251     }
252 
253     return priorities.at(priority()) > priorities.at(right.priority());
254 }
255 
256 } // namespace src
257 } // namespace pels
258 } // namespace openpower
259