blob: 07a92c3e6912183cc0149a71ac19892641fd3e72 [file] [log] [blame]
Shawn McCarney6a957f62024-01-10 16:15:19 -06001/**
2 * Copyright © 2024 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
17#include "config_file_parser.hpp"
18
19#include "config_file_parser_error.hpp"
Shawn McCarney7b7a5632025-11-19 13:08:58 -060020#include "gpios_only_device.hpp"
Shawn McCarney38f85002025-10-31 17:59:36 -050021#include "json_parser_utils.hpp"
Shawn McCarney4840b252025-11-06 16:06:40 -060022#include "ucd90160_device.hpp"
23#include "ucd90320_device.hpp"
Shawn McCarney6a957f62024-01-10 16:15:19 -060024
Shawn McCarney38f85002025-10-31 17:59:36 -050025#include <cstdint>
Shawn McCarney6a957f62024-01-10 16:15:19 -060026#include <exception>
27#include <fstream>
28#include <optional>
Shawn McCarney38f85002025-10-31 17:59:36 -050029#include <stdexcept>
Shawn McCarney6a957f62024-01-10 16:15:19 -060030
Shawn McCarney38f85002025-10-31 17:59:36 -050031using namespace phosphor::power::json_parser_utils;
32using ConfigFileParserError = phosphor::power::util::ConfigFileParserError;
Shawn McCarneye9144ab2024-05-22 12:34:18 -050033namespace fs = std::filesystem;
Shawn McCarney6a957f62024-01-10 16:15:19 -060034
35namespace phosphor::power::sequencer::config_file_parser
36{
37
Shawn McCarneye9144ab2024-05-22 12:34:18 -050038const std::filesystem::path standardConfigFileDirectory{
39 "/usr/share/phosphor-power-sequencer"};
40
Patrick Williams92261f82025-02-01 08:22:34 -050041std::filesystem::path find(
42 const std::vector<std::string>& compatibleSystemTypes,
43 const std::filesystem::path& configFileDir)
Shawn McCarneye9144ab2024-05-22 12:34:18 -050044{
45 fs::path pathName, possiblePath;
46 std::string fileName;
47
48 for (const std::string& systemType : compatibleSystemTypes)
49 {
50 // Look for file name that is entire system type + ".json"
51 // Example: com.acme.Hardware.Chassis.Model.MegaServer.json
52 fileName = systemType + ".json";
53 possiblePath = configFileDir / fileName;
54 if (fs::is_regular_file(possiblePath))
55 {
56 pathName = possiblePath;
57 break;
58 }
59
60 // Look for file name that is last node of system type + ".json"
61 // Example: MegaServer.json
62 std::string::size_type pos = systemType.rfind('.');
63 if ((pos != std::string::npos) && ((systemType.size() - pos) > 1))
64 {
65 fileName = systemType.substr(pos + 1) + ".json";
66 possiblePath = configFileDir / fileName;
67 if (fs::is_regular_file(possiblePath))
68 {
69 pathName = possiblePath;
70 break;
71 }
72 }
73 }
74
75 return pathName;
76}
77
Shawn McCarneyf1d20602025-11-12 17:27:48 -060078std::vector<std::unique_ptr<Chassis>> parse(
79 const std::filesystem::path& pathName, Services& services)
Shawn McCarney6a957f62024-01-10 16:15:19 -060080{
81 try
82 {
83 // Use standard JSON parser to create tree of JSON elements
84 std::ifstream file{pathName};
85 json rootElement = json::parse(file);
86
87 // Parse tree of JSON elements and return corresponding C++ objects
Shawn McCarneyf1d20602025-11-12 17:27:48 -060088 return internal::parseRoot(rootElement, services);
Shawn McCarney6a957f62024-01-10 16:15:19 -060089 }
90 catch (const std::exception& e)
91 {
92 throw ConfigFileParserError{pathName, e.what()};
93 }
94}
95
96namespace internal
97{
98
Shawn McCarneye02aa3c2025-11-10 12:25:54 -060099std::unique_ptr<Chassis> parseChassis(
Shawn McCarney1cd07b82025-11-12 13:29:26 -0600100 const json& element,
101 const std::map<std::string, JSONRefWrapper>& chassisTemplates,
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600102 Services& services)
103{
104 verifyIsObject(element);
105
106 // If chassis object is not using a template, parse properties normally
107 if (!element.contains("template_id"))
108 {
109 bool isChassisTemplate{false};
110 return parseChassisProperties(element, isChassisTemplate, NO_VARIABLES,
111 services);
112 }
113
114 // Parse chassis object that is using a template
115 unsigned int propertyCount{0};
116
117 // Optional comments property; value not stored
118 if (element.contains("comments"))
119 {
120 ++propertyCount;
121 }
122
123 // Required template_id property
124 const json& templateIDElement = getRequiredProperty(element, "template_id");
125 std::string templateID = parseString(templateIDElement);
126 ++propertyCount;
127
128 // Required template_variable_values property
129 const json& variablesElement =
130 getRequiredProperty(element, "template_variable_values");
131 std::map<std::string, std::string> variables =
132 parseVariables(variablesElement);
133 ++propertyCount;
134
135 // Verify no invalid properties exist
136 verifyPropertyCount(element, propertyCount);
137
138 // Get reference to chassis template JSON
139 auto it = chassisTemplates.find(templateID);
140 if (it == chassisTemplates.end())
141 {
142 throw std::invalid_argument{
143 "Invalid chassis template id: " + templateID};
144 }
145 const json& templateElement = it->second.get();
146
147 // Parse properties in template using variable values for this chassis
148 bool isChassisTemplate{true};
149 return parseChassisProperties(templateElement, isChassisTemplate, variables,
150 services);
151}
152
153std::vector<std::unique_ptr<Chassis>> parseChassisArray(
Shawn McCarney1cd07b82025-11-12 13:29:26 -0600154 const json& element,
155 const std::map<std::string, JSONRefWrapper>& chassisTemplates,
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600156 Services& services)
157{
158 verifyIsArray(element);
159 std::vector<std::unique_ptr<Chassis>> chassis;
160 for (auto& chassisElement : element)
161 {
162 chassis.emplace_back(
163 parseChassis(chassisElement, chassisTemplates, services));
164 }
165 return chassis;
166}
167
168std::unique_ptr<Chassis> parseChassisProperties(
169 const json& element, bool isChassisTemplate,
170 const std::map<std::string, std::string>& variables, Services& services)
171
172{
173 verifyIsObject(element);
174 unsigned int propertyCount{0};
175
176 // Optional comments property; value not stored
177 if (element.contains("comments"))
178 {
179 ++propertyCount;
180 }
181
182 // Required id property if this is a chassis template
183 // Don't parse again; this was already parsed by parseChassisTemplate()
184 if (isChassisTemplate)
185 {
186 getRequiredProperty(element, "id");
187 ++propertyCount;
188 }
189
190 // Required number property
191 const json& numberElement = getRequiredProperty(element, "number");
192 unsigned int number = parseUnsignedInteger(numberElement, variables);
193 if (number < 1)
194 {
195 throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
196 }
197 ++propertyCount;
198
199 // Required inventory_path property
200 const json& inventoryPathElement =
201 getRequiredProperty(element, "inventory_path");
202 std::string inventoryPath =
203 parseString(inventoryPathElement, false, variables);
204 ++propertyCount;
205
206 // Required power_sequencers property
207 const json& powerSequencersElement =
208 getRequiredProperty(element, "power_sequencers");
209 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers =
210 parsePowerSequencerArray(powerSequencersElement, variables, services);
211 ++propertyCount;
212
213 // Verify no invalid properties exist
214 verifyPropertyCount(element, propertyCount);
215
216 return std::make_unique<Chassis>(number, inventoryPath,
217 std::move(powerSequencers));
218}
219
Shawn McCarneyfb7e0932025-11-10 10:23:05 -0600220std::tuple<std::string, JSONRefWrapper> parseChassisTemplate(
221 const json& element)
222{
223 verifyIsObject(element);
224 unsigned int propertyCount{0};
225
226 // Optional comments property; value not stored
227 if (element.contains("comments"))
228 {
229 ++propertyCount;
230 }
231
232 // Required id property
233 const json& idElement = getRequiredProperty(element, "id");
234 std::string id = parseString(idElement);
235 ++propertyCount;
236
237 // Required number property
238 // Just verify it exists; cannot be parsed without variable values
239 getRequiredProperty(element, "number");
240 ++propertyCount;
241
242 // Required inventory_path property
243 // Just verify it exists; cannot be parsed without variable values
244 getRequiredProperty(element, "inventory_path");
245 ++propertyCount;
246
247 // Required power_sequencers property
248 // Just verify it exists; cannot be parsed without variable values
249 getRequiredProperty(element, "power_sequencers");
250 ++propertyCount;
251
252 // Verify no invalid properties exist
253 verifyPropertyCount(element, propertyCount);
254
255 return {id, JSONRefWrapper{element}};
256}
257
Shawn McCarney1cd07b82025-11-12 13:29:26 -0600258std::map<std::string, JSONRefWrapper> parseChassisTemplateArray(
259 const json& element)
260{
261 verifyIsArray(element);
262 std::map<std::string, JSONRefWrapper> chassisTemplates;
263 for (auto& chassisTemplateElement : element)
264 {
265 chassisTemplates.emplace(parseChassisTemplate(chassisTemplateElement));
266 }
267 return chassisTemplates;
268}
269
Shawn McCarney984be692025-11-18 16:38:29 -0600270PgoodGPIO parseGPIO(const json& element,
271 const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600272{
273 verifyIsObject(element);
274 unsigned int propertyCount{0};
275
276 // Required line property
277 const json& lineElement = getRequiredProperty(element, "line");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600278 unsigned int line = parseUnsignedInteger(lineElement, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600279 ++propertyCount;
280
281 // Optional active_low property
282 bool activeLow{false};
283 auto activeLowIt = element.find("active_low");
284 if (activeLowIt != element.end())
285 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600286 activeLow = parseBoolean(*activeLowIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600287 ++propertyCount;
288 }
289
290 // Verify no invalid properties exist
291 verifyPropertyCount(element, propertyCount);
292
Shawn McCarney984be692025-11-18 16:38:29 -0600293 return PgoodGPIO(line, activeLow);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600294}
295
Shawn McCarneye140ed92025-11-06 11:20:38 -0600296std::tuple<uint8_t, uint16_t> parseI2CInterface(
297 const nlohmann::json& element,
298 const std::map<std::string, std::string>& variables)
299{
300 verifyIsObject(element);
301 unsigned int propertyCount{0};
302
303 // Required bus property
304 const json& busElement = getRequiredProperty(element, "bus");
305 uint8_t bus = parseUint8(busElement, variables);
306 ++propertyCount;
307
308 // Required address property
309 const json& addressElement = getRequiredProperty(element, "address");
310 uint16_t address = parseHexByte(addressElement, variables);
311 ++propertyCount;
312
313 // Verify no invalid properties exist
314 verifyPropertyCount(element, propertyCount);
315
316 return {bus, address};
317}
318
Shawn McCarney4840b252025-11-06 16:06:40 -0600319std::unique_ptr<PowerSequencerDevice> parsePowerSequencer(
320 const nlohmann::json& element,
321 const std::map<std::string, std::string>& variables, Services& services)
322{
323 verifyIsObject(element);
324 unsigned int propertyCount{0};
325
326 // Optional comments property; value not stored
327 if (element.contains("comments"))
328 {
329 ++propertyCount;
330 }
331
332 // Required type property
333 const json& typeElement = getRequiredProperty(element, "type");
334 std::string type = parseString(typeElement, false, variables);
335 ++propertyCount;
336
Shawn McCarney7b7a5632025-11-19 13:08:58 -0600337 // i2c_interface property is required for some device types
338 uint8_t bus{0};
339 uint16_t address{0};
340 auto i2cInterfaceIt = element.find("i2c_interface");
341 if (i2cInterfaceIt != element.end())
342 {
343 std::tie(bus, address) = parseI2CInterface(*i2cInterfaceIt, variables);
344 ++propertyCount;
345 }
346 else if (type != GPIOsOnlyDevice::deviceName)
347 {
348 throw std::invalid_argument{"Required property missing: i2c_interface"};
349 }
Shawn McCarney4840b252025-11-06 16:06:40 -0600350
351 // Required power_control_gpio_name property
352 const json& powerControlGPIONameElement =
353 getRequiredProperty(element, "power_control_gpio_name");
354 std::string powerControlGPIOName =
355 parseString(powerControlGPIONameElement, false, variables);
356 ++propertyCount;
357
358 // Required power_good_gpio_name property
359 const json& powerGoodGPIONameElement =
360 getRequiredProperty(element, "power_good_gpio_name");
361 std::string powerGoodGPIOName =
362 parseString(powerGoodGPIONameElement, false, variables);
363 ++propertyCount;
364
Shawn McCarney7b7a5632025-11-19 13:08:58 -0600365 // rails property is required for some device types
366 std::vector<std::unique_ptr<Rail>> rails{};
367 auto railsIt = element.find("rails");
368 if (railsIt != element.end())
369 {
370 rails = parseRailArray(*railsIt, variables);
371 ++propertyCount;
372 }
373 else if (type != GPIOsOnlyDevice::deviceName)
374 {
375 throw std::invalid_argument{"Required property missing: rails"};
376 }
Shawn McCarney4840b252025-11-06 16:06:40 -0600377
378 // Verify no invalid properties exist
379 verifyPropertyCount(element, propertyCount);
380
381 if (type == UCD90160Device::deviceName)
382 {
383 return std::make_unique<UCD90160Device>(
384 bus, address, powerControlGPIOName, powerGoodGPIOName,
385 std::move(rails), services);
386 }
387 else if (type == UCD90320Device::deviceName)
388 {
389 return std::make_unique<UCD90320Device>(
390 bus, address, powerControlGPIOName, powerGoodGPIOName,
391 std::move(rails), services);
392 }
Shawn McCarney7b7a5632025-11-19 13:08:58 -0600393 else if (type == GPIOsOnlyDevice::deviceName)
394 {
395 return std::make_unique<GPIOsOnlyDevice>(powerControlGPIOName,
396 powerGoodGPIOName);
397 }
Shawn McCarney4840b252025-11-06 16:06:40 -0600398 throw std::invalid_argument{"Invalid power sequencer type: " + type};
399}
400
401std::vector<std::unique_ptr<PowerSequencerDevice>> parsePowerSequencerArray(
402 const nlohmann::json& element,
403 const std::map<std::string, std::string>& variables, Services& services)
404{
405 verifyIsArray(element);
406 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers;
407 for (auto& powerSequencerElement : element)
408 {
409 powerSequencers.emplace_back(
410 parsePowerSequencer(powerSequencerElement, variables, services));
411 }
412 return powerSequencers;
413}
414
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600415std::unique_ptr<Rail> parseRail(
416 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600417{
418 verifyIsObject(element);
419 unsigned int propertyCount{0};
420
421 // Required name property
422 const json& nameElement = getRequiredProperty(element, "name");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600423 std::string name = parseString(nameElement, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600424 ++propertyCount;
425
426 // Optional presence property
427 std::optional<std::string> presence{};
428 auto presenceIt = element.find("presence");
429 if (presenceIt != element.end())
430 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600431 presence = parseString(*presenceIt, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600432 ++propertyCount;
433 }
434
435 // Optional page property
436 std::optional<uint8_t> page{};
437 auto pageIt = element.find("page");
438 if (pageIt != element.end())
439 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600440 page = parseUint8(*pageIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600441 ++propertyCount;
442 }
443
Shawn McCarney16e493a2024-01-29 14:20:32 -0600444 // Optional is_power_supply_rail property
445 bool isPowerSupplyRail{false};
446 auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
447 if (isPowerSupplyRailIt != element.end())
448 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600449 isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt, variables);
Shawn McCarney16e493a2024-01-29 14:20:32 -0600450 ++propertyCount;
451 }
452
Shawn McCarney6a957f62024-01-10 16:15:19 -0600453 // Optional check_status_vout property
454 bool checkStatusVout{false};
455 auto checkStatusVoutIt = element.find("check_status_vout");
456 if (checkStatusVoutIt != element.end())
457 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600458 checkStatusVout = parseBoolean(*checkStatusVoutIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600459 ++propertyCount;
460 }
461
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600462 // Optional compare_voltage_to_limit property
463 bool compareVoltageToLimit{false};
464 auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit");
465 if (compareVoltageToLimitIt != element.end())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600466 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600467 compareVoltageToLimit =
468 parseBoolean(*compareVoltageToLimitIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600469 ++propertyCount;
470 }
471
472 // Optional gpio property
Shawn McCarney984be692025-11-18 16:38:29 -0600473 std::optional<PgoodGPIO> gpio{};
Shawn McCarney6a957f62024-01-10 16:15:19 -0600474 auto gpioIt = element.find("gpio");
475 if (gpioIt != element.end())
476 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600477 gpio = parseGPIO(*gpioIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600478 ++propertyCount;
479 }
480
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600481 // If check_status_vout or compare_voltage_to_limit property is true, the
482 // page property is required; verify page was specified
483 if ((checkStatusVout || compareVoltageToLimit) && !page.has_value())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600484 {
485 throw std::invalid_argument{"Required property missing: page"};
486 }
487
488 // Verify no invalid properties exist
489 verifyPropertyCount(element, propertyCount);
490
Shawn McCarney16e493a2024-01-29 14:20:32 -0600491 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600492 checkStatusVout, compareVoltageToLimit, gpio);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600493}
494
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600495std::vector<std::unique_ptr<Rail>> parseRailArray(
496 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600497{
498 verifyIsArray(element);
499 std::vector<std::unique_ptr<Rail>> rails;
500 for (auto& railElement : element)
501 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600502 rails.emplace_back(parseRail(railElement, variables));
Shawn McCarney6a957f62024-01-10 16:15:19 -0600503 }
504 return rails;
505}
506
Shawn McCarneyf1d20602025-11-12 17:27:48 -0600507std::vector<std::unique_ptr<Chassis>> parseRoot(const json& element,
508 Services& services)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600509{
510 verifyIsObject(element);
511 unsigned int propertyCount{0};
512
Shawn McCarneyf1d20602025-11-12 17:27:48 -0600513 // Optional comments property; value not stored
514 if (element.contains("comments"))
515 {
516 ++propertyCount;
517 }
518
519 // Optional chassis_templates property
520 std::map<std::string, JSONRefWrapper> chassisTemplates{};
521 auto chassisTemplatesIt = element.find("chassis_templates");
522 if (chassisTemplatesIt != element.end())
523 {
524 chassisTemplates = parseChassisTemplateArray(*chassisTemplatesIt);
525 ++propertyCount;
526 }
527
528 // Required chassis property
529 const json& chassisElement = getRequiredProperty(element, "chassis");
530 std::vector<std::unique_ptr<Chassis>> chassis =
531 parseChassisArray(chassisElement, chassisTemplates, services);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600532 ++propertyCount;
533
534 // Verify no invalid properties exist
535 verifyPropertyCount(element, propertyCount);
536
Shawn McCarneyf1d20602025-11-12 17:27:48 -0600537 return chassis;
Shawn McCarney6a957f62024-01-10 16:15:19 -0600538}
539
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600540std::map<std::string, std::string> parseVariables(const json& element)
541{
542 verifyIsObject(element);
543
544 std::map<std::string, std::string> variables;
545 std::string name, value;
546 for (const auto& [nameElement, valueElement] : element.items())
547 {
548 name = parseString(nameElement);
549 value = parseString(valueElement);
550 variables.emplace(name, value);
551 }
552 return variables;
553}
554
Shawn McCarney6a957f62024-01-10 16:15:19 -0600555} // namespace internal
556
557} // namespace phosphor::power::sequencer::config_file_parser