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