1 extern "C" 2 { 3 #include <libpdbg.h> 4 } 5 6 #include "attributes_info.H" 7 8 #include "extensions/phal/common_utils.hpp" 9 #include "extensions/phal/create_pel.hpp" 10 #include "extensions/phal/phal_error.hpp" 11 #include "util.hpp" 12 13 #include <fmt/format.h> 14 #include <libekb.H> 15 16 #include <ext_interface.hpp> 17 #include <nlohmann/json.hpp> 18 #include <phosphor-logging/log.hpp> 19 #include <registration.hpp> 20 21 namespace openpower 22 { 23 namespace phal 24 { 25 26 using namespace phosphor::logging; 27 28 /** 29 * @brief Select BOOT SEEPROM and Measurement SEEPROM(PRIMARY/BACKUP) on POWER 30 * processor position 0/1 depending on boot count before kicking off 31 * the boot. 32 * 33 * @return void 34 */ 35 void selectBootSeeprom() 36 { 37 struct pdbg_target* procTarget; 38 ATTR_BACKUP_SEEPROM_SELECT_Enum bkpSeePromSelect; 39 ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_Enum bkpMeaSeePromSelect; 40 41 pdbg_for_each_class_target("proc", procTarget) 42 { 43 if (!isPrimaryProc(procTarget)) 44 { 45 continue; 46 } 47 48 // Choose seeprom side to boot from based on boot count 49 if (getBootCount() > 0) 50 { 51 log<level::INFO>("Setting SBE seeprom side to 0", 52 entry("SBE_SIDE_SELECT=%d", 53 ENUM_ATTR_BACKUP_SEEPROM_SELECT_PRIMARY)); 54 55 bkpSeePromSelect = ENUM_ATTR_BACKUP_SEEPROM_SELECT_PRIMARY; 56 bkpMeaSeePromSelect = 57 ENUM_ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_PRIMARY; 58 } 59 else 60 { 61 log<level::INFO>("Setting SBE seeprom side to 1", 62 entry("SBE_SIDE_SELECT=%d", 63 ENUM_ATTR_BACKUP_SEEPROM_SELECT_SECONDARY)); 64 bkpSeePromSelect = ENUM_ATTR_BACKUP_SEEPROM_SELECT_SECONDARY; 65 bkpMeaSeePromSelect = 66 ENUM_ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_SECONDARY; 67 } 68 69 // Set the Attribute as per bootcount policy for boot seeprom 70 if (DT_SET_PROP(ATTR_BACKUP_SEEPROM_SELECT, procTarget, 71 bkpSeePromSelect)) 72 { 73 log<level::ERR>( 74 "Attribute [ATTR_BACKUP_SEEPROM_SELECT] set failed"); 75 throw std::runtime_error( 76 "Attribute [ATTR_BACKUP_SEEPROM_SELECT] set failed"); 77 } 78 79 // Set the Attribute as per bootcount policy for measurement seeprom 80 if (DT_SET_PROP(ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT, procTarget, 81 bkpMeaSeePromSelect)) 82 { 83 log<level::ERR>( 84 "Attribute [ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT] set " 85 "failed"); 86 throw std::runtime_error( 87 "Attribute [ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT] set " 88 "failed"); 89 } 90 } 91 } 92 93 /** 94 * @brief Read the HW Level from VPD and set CLK NE termination site 95 * Note any failure in this function will result startHost failure. 96 */ 97 void setClkNETerminationSite() 98 { 99 // Get Motherborad VINI Recored "HW" keyword 100 constexpr auto objPath = 101 "/xyz/openbmc_project/inventory/system/chassis/motherboard"; 102 constexpr auto kwdVpdInf = "com.ibm.ipzvpd.VINI"; 103 constexpr auto hwKwd = "HW"; 104 105 auto bus = sdbusplus::bus::new_default(); 106 107 std::string service = util::getService(bus, objPath, kwdVpdInf); 108 109 auto properties = bus.new_method_call( 110 service.c_str(), objPath, "org.freedesktop.DBus.Properties", "Get"); 111 properties.append(kwdVpdInf); 112 properties.append(hwKwd); 113 114 // Store "HW" Keyword data. 115 std::variant<std::vector<uint8_t>> val; 116 try 117 { 118 auto result = bus.call(properties); 119 result.read(val); 120 } 121 catch (const sdbusplus::exception::exception& e) 122 { 123 log<level::ERR>("Get HW Keyword read from VINI Failed"); 124 throw std::runtime_error("Get HW Keyword read from VINI Failed"); 125 } 126 127 auto hwData = std::get<std::vector<uint8_t>>(val); 128 129 //"HW" Keyword size is 2 as per VPD spec. 130 constexpr auto hwKwdSize = 2; 131 if (hwKwdSize != hwData.size()) 132 { 133 log<level::ERR>( 134 fmt::format("Incorrect VINI records HW Keyword data size({})", 135 hwData.size()) 136 .c_str()); 137 throw std::runtime_error("Incorrect VINI records HW Keyword data size"); 138 } 139 140 log<level::DEBUG>(fmt::format("VINI Records HW[0]:{} HW[1]:{}", 141 hwData.at(0), hwData.at(1)) 142 .c_str()); 143 144 // VINI Record "HW" keyword's Byte 0's MSB bit indicates 145 // proc or planar type need to choose. 146 constexpr uint8_t SYS_CLK_NE_TERMINATION_ON_MASK = 0x80; 147 148 ATTR_SYS_CLK_NE_TERMINATION_SITE_Type clockTerm = 149 ENUM_ATTR_SYS_CLK_NE_TERMINATION_SITE_PLANAR; 150 151 if (SYS_CLK_NE_TERMINATION_ON_MASK & hwData.at(0)) 152 { 153 clockTerm = ENUM_ATTR_SYS_CLK_NE_TERMINATION_SITE_PROC; 154 } 155 156 // update all the processor attributes 157 struct pdbg_target* procTarget; 158 pdbg_for_each_class_target("proc", procTarget) 159 { 160 161 if (DT_SET_PROP(ATTR_SYS_CLK_NE_TERMINATION_SITE, procTarget, 162 clockTerm)) 163 { 164 log<level::ERR>( 165 "Attribute ATTR_SYS_CLK_NE_TERMINATION_SITE set failed"); 166 throw std::runtime_error( 167 "Attribute ATTR_SYS_CLK_NE_TERMINATION_SITE set failed"); 168 } 169 } 170 } 171 172 /** 173 * @brief Helper function to create error log (aka PEL) with 174 * procedure callout for the hardware isolation policy 175 * settings failures. 176 * 177 * @param[in] procedureCode - The procedure code to include in the callout 178 * @param[in] priority - The priority for the procedure callout 179 * @param[in] additionalData - The additional data to include in the error log 180 * 181 * @return void 182 */ 183 static void 184 createPELForHwIsolationSettingsErr(const std::string& procedureCode, 185 const std::string& priority, 186 const pel::FFDCData& additionalData) 187 { 188 try 189 { 190 using json = nlohmann::json; 191 using Severity = 192 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level; 193 194 json jsonCalloutDataList; 195 jsonCalloutDataList = json::array(); 196 json jsonCalloutData; 197 jsonCalloutData["Procedure"] = procedureCode; 198 jsonCalloutData["Priority"] = priority; 199 jsonCalloutDataList.emplace_back(jsonCalloutData); 200 201 openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", 202 jsonCalloutDataList, additionalData, 203 Severity::Error); 204 } 205 catch (const std::exception& e) 206 { 207 // Don't throw exception since the caller might call in the error path 208 // and even we should allow the hardware isolation by default. 209 log<level::ERR>( 210 fmt::format("Exception [{}], failed to create the error log " 211 "for the hardware isolation policy settings failures.", 212 e.what()) 213 .c_str()); 214 } 215 } 216 217 /** 218 * @brief Helper function to decide the hardware isolation (aka guard) 219 * 220 * @return xyz.openbmc_project.Object.Enable::Enabled value on success 221 * true on failure since hardware isolation feature should be 222 * enabled by default. 223 */ 224 static bool allowHwIsolation() 225 { 226 bool allowHwIsolation{true}; 227 228 constexpr auto hwIsolationPolicyObjPath = 229 "/xyz/openbmc_project/hardware_isolation/allow_hw_isolation"; 230 constexpr auto hwIsolationPolicyIface = "xyz.openbmc_project.Object.Enable"; 231 232 try 233 { 234 auto bus = sdbusplus::bus::new_default(); 235 236 std::string service = util::getService(bus, hwIsolationPolicyObjPath, 237 hwIsolationPolicyIface); 238 239 auto method = 240 bus.new_method_call(service.c_str(), hwIsolationPolicyObjPath, 241 "org.freedesktop.DBus.Properties", "Get"); 242 method.append(hwIsolationPolicyIface, "Enabled"); 243 244 auto reply = bus.call(method); 245 246 std::variant<bool> resp; 247 248 reply.read(resp); 249 250 if (const bool* enabledPropVal = std::get_if<bool>(&resp)) 251 { 252 allowHwIsolation = *enabledPropVal; 253 } 254 else 255 { 256 const auto trace{fmt::format( 257 "Failed to read the HardwareIsolation policy " 258 "from the path [{}] interface [{}]. Continuing with " 259 "default mode(allow_hw_isolation)", 260 hwIsolationPolicyObjPath, hwIsolationPolicyIface)}; 261 262 log<level::ERR>(trace.c_str()); 263 createPELForHwIsolationSettingsErr("BMC0001", "M", 264 {{"REASON_FOR_PEL", trace}}); 265 } 266 } 267 catch (const sdbusplus::exception::exception& e) 268 { 269 const auto trace{fmt::format( 270 "Exception [{}] to get the HardwareIsolation policy " 271 "from the path [{}] interface [{}]. Continuing with " 272 "default mode (allow_hw_isolation)", 273 e.what(), hwIsolationPolicyObjPath, hwIsolationPolicyIface)}; 274 275 log<level::ERR>(trace.c_str()); 276 createPELForHwIsolationSettingsErr("BMC0001", "M", 277 {{"REASON_FOR_PEL", trace}}); 278 } 279 280 return allowHwIsolation; 281 } 282 283 /** 284 * @brief Starts the self boot engine on POWER processor position 0 285 * to kick off a boot. 286 * @return void 287 */ 288 void startHost(enum ipl_type iplType = IPL_TYPE_NORMAL) 289 { 290 try 291 { 292 phal_init(); 293 ipl_set_type(iplType); 294 295 /** 296 * Don't apply guard records if the HardwareIsolation (aka guard) 297 * the policy is disabled (false). By default, libipl will apply 298 * guard records. 299 */ 300 if (!allowHwIsolation()) 301 { 302 ipl_disable_guard(); 303 } 304 305 if (iplType == IPL_TYPE_NORMAL) 306 { 307 // Update SEEPROM side only for NORMAL boot 308 selectBootSeeprom(); 309 } 310 setClkNETerminationSite(); 311 } 312 catch (const std::exception& ex) 313 { 314 log<level::ERR>("Exception raised during ipl initialisation", 315 entry("EXCEPTION=%s", ex.what())); 316 openpower::pel::createPEL("org.open_power.PHAL.Error.Boot"); 317 openpower::pel::detail::processBootError(false); 318 throw std::runtime_error("IPL initialization failed"); 319 } 320 321 // To clear trace if success 322 openpower::pel::detail::processBootError(true); 323 324 // callback method will be called upon failure which will create the PEL 325 int rc = ipl_run_major(0); 326 if (rc > 0) 327 { 328 log<level::ERR>("step 0 failed to start the host"); 329 throw std::runtime_error("Failed to execute host start boot step"); 330 } 331 } 332 333 /** 334 * @brief Starts the reboot with type memory preserving reboot. 335 * @return void 336 */ 337 void startHostMpReboot() 338 { 339 // set ipl type as mpipl 340 startHost(IPL_TYPE_MPIPL); 341 } 342 343 /** 344 * @brief Starts the normal boot type. 345 * @return void 346 */ 347 void startHostNormal() 348 { 349 startHost(IPL_TYPE_NORMAL); 350 } 351 352 REGISTER_PROCEDURE("startHost", startHostNormal) 353 REGISTER_PROCEDURE("startHostMpReboot", startHostMpReboot) 354 355 } // namespace phal 356 } // namespace openpower 357