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 
192         json jsonCalloutDataList;
193         jsonCalloutDataList = json::array();
194         json jsonCalloutData;
195         jsonCalloutData["Procedure"] = procedureCode;
196         jsonCalloutData["Priority"] = priority;
197         jsonCalloutDataList.emplace_back(jsonCalloutData);
198 
199         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot",
200                                        jsonCalloutDataList, additionalData);
201     }
202     catch (const std::exception& e)
203     {
204         // Don't throw exception since the caller might call in the error path
205         // and even we should allow the hardware isolation by default.
206         log<level::ERR>(
207             fmt::format("Exception [{}], failed to create the error log "
208                         "for the hardware isolation policy settings failures.",
209                         e.what())
210                 .c_str());
211     }
212 }
213 
214 /**
215  * @brief Helper function to decide the hardware isolation (aka guard)
216  *
217  * @return xyz.openbmc_project.Object.Enable::Enabled value on success
218  *         true on failure since hardware isolation feature should be
219  *         enabled by default.
220  */
221 static bool allowHwIsolation()
222 {
223     bool allowHwIsolation{true};
224 
225     constexpr auto hwIsolationPolicyObjPath =
226         "/xyz/openbmc_project/hardware_isolation/allow_hw_isolation";
227     constexpr auto hwIsolationPolicyIface = "xyz.openbmc_project.Object.Enable";
228 
229     try
230     {
231         auto bus = sdbusplus::bus::new_default();
232 
233         std::string service = util::getService(bus, hwIsolationPolicyObjPath,
234                                                hwIsolationPolicyIface);
235 
236         auto method =
237             bus.new_method_call(service.c_str(), hwIsolationPolicyObjPath,
238                                 "org.freedesktop.DBus.Properties", "Get");
239         method.append(hwIsolationPolicyIface, "Enabled");
240 
241         auto reply = bus.call(method);
242 
243         std::variant<bool> resp;
244 
245         reply.read(resp);
246 
247         if (const bool* enabledPropVal = std::get_if<bool>(&resp))
248         {
249             allowHwIsolation = *enabledPropVal;
250         }
251         else
252         {
253             const auto trace{fmt::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::exception& e)
265     {
266         const auto trace{fmt::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  */
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  */
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  */
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