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  */
183 static void
createPELForHwIsolationSettingsErr(const std::string & procedureCode,const std::string & priority,const pel::FFDCData & additionalData)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             std::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  */
allowHwIsolation()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{std::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_t& e)
268     {
269         const auto trace{std::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  */
startHost(enum ipl_type iplType=IPL_TYPE_NORMAL)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  */
startHostMpReboot()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  */
startHostNormal()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