1 /** 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 "services.hpp" 18 19 #include "types.hpp" 20 #include "utility.hpp" 21 22 #include <sys/types.h> // for getpid() 23 #include <unistd.h> // for getpid() 24 25 #include <gpiod.hpp> 26 27 #include <exception> 28 #include <variant> 29 30 namespace phosphor::power::sequencer 31 { 32 33 void BMCServices::logError(const std::string& message, Entry::Level severity, 34 std::map<std::string, std::string>& additionalData) 35 { 36 try 37 { 38 // Add PID to AdditionalData 39 additionalData.emplace("_PID", std::to_string(getpid())); 40 41 // If severity is critical, set error as system terminating 42 if (severity == Entry::Level::Critical) 43 { 44 additionalData.emplace("SEVERITY_DETAIL", "SYSTEM_TERM"); 45 } 46 47 auto method = bus.new_method_call( 48 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 49 "xyz.openbmc_project.Logging.Create", "Create"); 50 method.append(message, severity, additionalData); 51 bus.call_noreply(method); 52 } 53 catch (const std::exception& e) 54 { 55 lg2::error("Unable to log error {ERROR}: {EXCEPTION}", "ERROR", message, 56 "EXCEPTION", e); 57 } 58 } 59 60 bool BMCServices::isPresent(const std::string& inventoryPath) 61 { 62 // Initially assume hardware is not present 63 bool present{false}; 64 65 // Try to find cached presence value 66 auto it = presenceCache.find(inventoryPath); 67 if (it != presenceCache.end()) 68 { 69 present = it->second; 70 } 71 else 72 { 73 // Get presence from D-Bus interface/property 74 try 75 { 76 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, 77 INVENTORY_MGR_IFACE, bus, present); 78 } 79 catch (const sdbusplus::exception_t& e) 80 { 81 // If exception type is expected and indicates hardware not present 82 if (isExpectedException(e)) 83 { 84 present = false; 85 } 86 else 87 { 88 // Re-throw unexpected exception 89 throw; 90 } 91 } 92 93 // Cache presence value 94 presenceCache[inventoryPath] = present; 95 } 96 97 return present; 98 } 99 100 std::vector<int> BMCServices::getGPIOValues(const std::string& chipLabel) 101 { 102 // Set up the chip object 103 gpiod::chip chip{chipLabel, gpiod::chip::OPEN_BY_LABEL}; 104 unsigned int numLines = chip.num_lines(); 105 lg2::info( 106 "Reading GPIO values from chip {NAME} with label {LABEL} and {NUM_LINES} lines", 107 "NAME", chip.name(), "LABEL", chipLabel, "NUM_LINES", numLines); 108 109 // Read GPIO values. Work around libgpiod bulk line maximum by getting 110 // values from individual lines. 111 std::vector<int> values; 112 for (unsigned int offset = 0; offset < numLines; ++offset) 113 { 114 gpiod::line line = chip.get_line(offset); 115 line.request({"phosphor-power-control", 116 gpiod::line_request::DIRECTION_INPUT, 0}); 117 values.push_back(line.get_value()); 118 line.release(); 119 } 120 return values; 121 } 122 123 bool BMCServices::isExpectedException(const sdbusplus::exception_t& e) 124 { 125 // Initially assume exception is not one of the expected types 126 bool isExpected{false}; 127 128 // If the D-Bus error name is set within the exception 129 if (e.name() != nullptr) 130 { 131 // Check if the error name is one of the expected values when hardware 132 // is not present. 133 // 134 // Sometimes the object path does not exist. Sometimes the object path 135 // exists, but it does not implement the D-Bus interface that contains 136 // the present property. Both of these cases result in exceptions. 137 // 138 // In the case where the interface is not implemented, the systemd 139 // documentation seems to indicate that the error name should be 140 // SD_BUS_ERROR_UNKNOWN_INTERFACE. However, in OpenBMC the 141 // SD_BUS_ERROR_UNKNOWN_PROPERTY error name can occur. 142 std::string name = e.name(); 143 if ((name == SD_BUS_ERROR_UNKNOWN_OBJECT) || 144 (name == SD_BUS_ERROR_UNKNOWN_INTERFACE) || 145 (name == SD_BUS_ERROR_UNKNOWN_PROPERTY)) 146 { 147 isExpected = true; 148 } 149 } 150 151 return isExpected; 152 } 153 154 void BMCServices::createBMCDump() 155 { 156 try 157 { 158 auto method = bus.new_method_call( 159 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump/bmc", 160 "xyz.openbmc_project.Dump.Create", "CreateDump"); 161 method.append( 162 std::vector< 163 std::pair<std::string, std::variant<std::string, uint64_t>>>()); 164 bus.call_noreply(method); 165 } 166 catch (const std::exception& e) 167 { 168 lg2::error("Unable to create BMC dump: {ERROR}", "ERROR", e); 169 } 170 } 171 172 } // namespace phosphor::power::sequencer 173