xref: /openbmc/bios-settings-mgr/src/manager.cpp (revision fae5732530cc4bb63acc93eb84e354f2c41bbb22)
1 /*
2  Copyright (c) 2020 Intel 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 
17 #include "manager.hpp"
18 
19 #include "manager_serialize.hpp"
20 #include "xyz/openbmc_project/BIOSConfig/Common/error.hpp"
21 #include "xyz/openbmc_project/Common/error.hpp"
22 
23 #include <boost/asio.hpp>
24 #include <phosphor-logging/elog-errors.hpp>
25 #include <phosphor-logging/lg2.hpp>
26 #include <sdbusplus/asio/connection.hpp>
27 #include <sdbusplus/asio/object_server.hpp>
28 
29 namespace bios_config
30 {
31 
32 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
33 using namespace sdbusplus::xyz::openbmc_project::BIOSConfig::Common::Error;
34 
35 void Manager::setAttribute(AttributeName attribute, AttributeValue value)
36 {
37     auto pendingAttrs = Base::pendingAttributes();
38     auto iter = pendingAttrs.find(attribute);
39 
40     if (iter != pendingAttrs.end())
41     {
42         std::get<1>(iter->second) = value;
43     }
44     else
45     {
46         Manager::PendingAttribute attributeValue;
47 
48         if (std::get_if<int64_t>(&value))
49         {
50             std::get<0>(attributeValue) = AttributeType::Integer;
51         }
52         else
53         {
54             std::get<0>(attributeValue) = AttributeType::String;
55         }
56 
57         std::get<1>(attributeValue) = value;
58         pendingAttrs.emplace(attribute, attributeValue);
59     }
60 
61     pendingAttributes(pendingAttrs);
62 }
63 
64 Manager::AttributeDetails Manager::getAttribute(AttributeName attribute)
65 {
66     Manager::AttributeDetails value;
67 
68     auto table = Base::baseBIOSTable();
69     auto iter = table.find(attribute);
70 
71     if (iter != table.end())
72     {
73         std::get<0>(value) =
74             std::get<static_cast<uint8_t>(Index::attributeType)>(iter->second);
75         std::get<1>(value) =
76             std::get<static_cast<uint8_t>(Index::currentValue)>(iter->second);
77 
78         auto pending = Base::pendingAttributes();
79         auto pendingIter = pending.find(attribute);
80         if (pendingIter != pending.end())
81         {
82             std::get<2>(value) = std::get<1>(pendingIter->second);
83         }
84         else if (std::get_if<std::string>(&std::get<1>(value)))
85         {
86             std::get<2>(value) = std::string();
87         }
88     }
89     else
90     {
91         throw AttributeNotFound();
92     }
93 
94     return value;
95 }
96 
97 Manager::BaseTable Manager::baseBIOSTable(BaseTable value)
98 {
99     pendingAttributes({});
100     auto baseTable = Base::baseBIOSTable(value, false);
101     serialize(*this, biosFile);
102     Base::resetBIOSSettings(Base::ResetFlag::NoAction);
103     return baseTable;
104 }
105 
106 bool Manager::validateEnumOption(
107     const std::string& attrValue,
108     const std::vector<std::tuple<BoundType, std::variant<int64_t, std::string>,
109                                  std::string>>& options)
110 {
111     for (const auto& enumOptions : options)
112     {
113         if ((BoundType::OneOf == std::get<0>(enumOptions)) &&
114             (attrValue == std::get<std::string>(std::get<1>(enumOptions))))
115         {
116             return true;
117         }
118     }
119 
120     lg2::error("No valid attribute");
121     return false;
122 }
123 
124 bool Manager::validateStringOption(
125     const std::string& attrValue,
126     const std::vector<std::tuple<BoundType, std::variant<int64_t, std::string>,
127                                  std::string>>& options)
128 {
129     size_t minStringLength = 0;
130     size_t maxStringLength = 0;
131     for (const auto& stringOptions : options)
132     {
133         if (BoundType::MinStringLength == std::get<0>(stringOptions))
134         {
135             minStringLength = std::get<int64_t>(std::get<1>(stringOptions));
136         }
137         else if (BoundType::MaxStringLength == std::get<0>(stringOptions))
138         {
139             maxStringLength = std::get<int64_t>(std::get<1>(stringOptions));
140         }
141         else
142         {
143             continue;
144         }
145     }
146 
147     if (attrValue.length() < minStringLength ||
148         attrValue.length() > maxStringLength)
149     {
150         lg2::error(
151             "{ATTRVALUE} Length is out of range, bound is invalid, maxStringLength = {MAXLEN}, minStringLength = {MINLEN}",
152             "ATTRVALUE", attrValue, "MAXLEN", maxStringLength, "MINLEN",
153             minStringLength);
154         return false;
155     }
156 
157     return true;
158 }
159 
160 bool Manager::validateIntegerOption(
161     const int64_t& attrValue,
162     const std::vector<std::tuple<BoundType, std::variant<int64_t, std::string>,
163                                  std::string>>& options)
164 {
165     int64_t lowerBound = 0;
166     int64_t upperBound = 0;
167     int64_t scalarIncrement = 0;
168 
169     for (const auto& integerOptions : options)
170     {
171         if (BoundType::LowerBound == std::get<0>(integerOptions))
172         {
173             lowerBound = std::get<int64_t>(std::get<1>(integerOptions));
174         }
175         else if (BoundType::UpperBound == std::get<0>(integerOptions))
176         {
177             upperBound = std::get<int64_t>(std::get<1>(integerOptions));
178         }
179         else if (BoundType::ScalarIncrement == std::get<0>(integerOptions))
180         {
181             scalarIncrement = std::get<int64_t>(std::get<1>(integerOptions));
182         }
183     }
184 
185     if ((attrValue < lowerBound) || (attrValue > upperBound))
186     {
187         lg2::error("Integer, bound is invalid");
188         return false;
189     }
190 
191     if (scalarIncrement == 0 ||
192         ((std::abs(attrValue - lowerBound)) % scalarIncrement) != 0)
193     {
194         lg2::error(
195             "((std::abs({ATTR_VALUE} - {LOWER_BOUND})) % {SCALAR_INCREMENT}) != 0",
196             "ATTR_VALUE", attrValue, "LOWER_BOUND", lowerBound,
197             "SCALAR_INCREMENT", scalarIncrement);
198         return false;
199     }
200 
201     return true;
202 }
203 
204 Manager::PendingAttributes Manager::pendingAttributes(PendingAttributes value)
205 {
206     // Clear the pending attributes
207     if (value.empty())
208     {
209         auto pendingAttrs = Base::pendingAttributes({}, false);
210         serialize(*this, biosFile);
211         return pendingAttrs;
212     }
213 
214     // Validate all the BIOS attributes before setting PendingAttributes
215     BaseTable biosTable = Base::baseBIOSTable();
216     for (const auto& pair : value)
217     {
218         auto iter = biosTable.find(pair.first);
219         // BIOS attribute not found in the BaseBIOSTable
220         if (iter == biosTable.end())
221         {
222             lg2::error("BIOS attribute not found in the BaseBIOSTable");
223             throw AttributeNotFound();
224         }
225 
226         auto attributeType =
227             std::get<static_cast<uint8_t>(Index::attributeType)>(iter->second);
228         if (attributeType != std::get<0>(pair.second))
229         {
230             lg2::error("attributeType is not same with bios base table");
231             throw InvalidArgument();
232         }
233 
234         // Validate enumeration BIOS attributes
235         if (attributeType == AttributeType::Enumeration)
236         {
237             // For enumeration the expected variant types is Enumeration
238             if (std::get<1>(pair.second).index() == 0)
239             {
240                 lg2::error("Enumeration property value is not enum");
241                 throw InvalidArgument();
242             }
243 
244             const auto& attrValue =
245                 std::get<std::string>(std::get<1>(pair.second));
246             const auto& options =
247                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
248 
249             if (!validateEnumOption(attrValue, options))
250             {
251                 throw InvalidArgument();
252             }
253         }
254 
255         if (attributeType == AttributeType::String)
256         {
257             // For enumeration the expected variant types is std::string
258             if (std::get<1>(pair.second).index() == 0)
259             {
260                 lg2::error("String property value is not string");
261                 throw InvalidArgument();
262             }
263 
264             const auto& attrValue =
265                 std::get<std::string>(std::get<1>(pair.second));
266             const auto& options =
267                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
268 
269             if (!validateStringOption(attrValue, options))
270             {
271                 throw InvalidArgument();
272             }
273         }
274 
275         if (attributeType == AttributeType::Integer)
276         {
277             // For enumeration the expected variant types is Integer
278             if (std::get<1>(pair.second).index() == 1)
279             {
280                 lg2::error("Enumeration property value is not int");
281                 throw InvalidArgument();
282             }
283 
284             const auto& attrValue = std::get<int64_t>(std::get<1>(pair.second));
285             const auto& options =
286                 std::get<static_cast<uint8_t>(Index::options)>(iter->second);
287 
288             if (!validateIntegerOption(attrValue, options))
289             {
290                 throw InvalidArgument();
291             }
292         }
293     }
294 
295     PendingAttributes pendingAttribute = Base::pendingAttributes();
296 
297     for (const auto& pair : value)
298     {
299         auto iter = pendingAttribute.find(pair.first);
300         if (iter != pendingAttribute.end())
301         {
302             iter = pendingAttribute.erase(iter);
303         }
304 
305         pendingAttribute.emplace(std::make_pair(pair.first, pair.second));
306     }
307 
308     auto pendingAttrs = Base::pendingAttributes(pendingAttribute, false);
309     serialize(*this, biosFile);
310 
311     return pendingAttrs;
312 }
313 
314 Manager::Manager(sdbusplus::asio::object_server& objectServer,
315                  std::shared_ptr<sdbusplus::asio::connection>& systemBus,
316                  std::string persistPath) :
317     sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager(
318         *systemBus, objectPath),
319     objServer(objectServer), systemBus(systemBus)
320 {
321     fs::path biosDir(persistPath);
322     fs::create_directories(biosDir);
323     biosFile = biosDir / biosPersistFile;
324     deserialize(biosFile, *this);
325 }
326 
327 } // namespace bios_config
328