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