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 <phosphor-logging/log.hpp>
18 #include <registration.hpp>
19 
20 namespace openpower
21 {
22 namespace phal
23 {
24 
25 using namespace phosphor::logging;
26 
27 /**
28  *  @brief  Select BOOT SEEPROM and Measurement SEEPROM(PRIMARY/BACKUP) on POWER
29  *          processor position 0/1 depending on boot count before kicking off
30  *          the boot.
31  *
32  *  @return void
33  */
34 void selectBootSeeprom()
35 {
36     struct pdbg_target* procTarget;
37     ATTR_BACKUP_SEEPROM_SELECT_Enum bkpSeePromSelect;
38     ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_Enum bkpMeaSeePromSelect;
39 
40     pdbg_for_each_class_target("proc", procTarget)
41     {
42         if (!isPrimaryProc(procTarget))
43         {
44             continue;
45         }
46 
47         // Choose seeprom side to boot from based on boot count
48         if (getBootCount() > 0)
49         {
50             log<level::INFO>("Setting SBE seeprom side to 0",
51                              entry("SBE_SIDE_SELECT=%d",
52                                    ENUM_ATTR_BACKUP_SEEPROM_SELECT_PRIMARY));
53 
54             bkpSeePromSelect = ENUM_ATTR_BACKUP_SEEPROM_SELECT_PRIMARY;
55             bkpMeaSeePromSelect =
56                 ENUM_ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_PRIMARY;
57         }
58         else
59         {
60             log<level::INFO>("Setting SBE seeprom side to 1",
61                              entry("SBE_SIDE_SELECT=%d",
62                                    ENUM_ATTR_BACKUP_SEEPROM_SELECT_SECONDARY));
63             bkpSeePromSelect = ENUM_ATTR_BACKUP_SEEPROM_SELECT_SECONDARY;
64             bkpMeaSeePromSelect =
65                 ENUM_ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT_SECONDARY;
66         }
67 
68         // Set the Attribute as per bootcount policy for boot seeprom
69         if (DT_SET_PROP(ATTR_BACKUP_SEEPROM_SELECT, procTarget,
70                         bkpSeePromSelect))
71         {
72             log<level::ERR>(
73                 "Attribute [ATTR_BACKUP_SEEPROM_SELECT] set failed");
74             throw std::runtime_error(
75                 "Attribute [ATTR_BACKUP_SEEPROM_SELECT] set failed");
76         }
77 
78         // Set the Attribute as per bootcount policy for measurement seeprom
79         if (DT_SET_PROP(ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT, procTarget,
80                         bkpMeaSeePromSelect))
81         {
82             log<level::ERR>(
83                 "Attribute [ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT] set "
84                 "failed");
85             throw std::runtime_error(
86                 "Attribute [ATTR_BACKUP_MEASUREMENT_SEEPROM_SELECT] set "
87                 "failed");
88         }
89     }
90 }
91 
92 /**
93  * @brief Read the HW Level from VPD and set CLK NE termination site
94  * Note any failure in this function will result startHost failure.
95  */
96 void setClkNETerminationSite()
97 {
98     // Get Motherborad VINI Recored "HW" keyword
99     constexpr auto objPath =
100         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
101     constexpr auto kwdVpdInf = "com.ibm.ipzvpd.VINI";
102     constexpr auto hwKwd = "HW";
103 
104     auto bus = sdbusplus::bus::new_default();
105 
106     std::string service = util::getService(bus, objPath, kwdVpdInf);
107 
108     auto properties = bus.new_method_call(
109         service.c_str(), objPath, "org.freedesktop.DBus.Properties", "Get");
110     properties.append(kwdVpdInf);
111     properties.append(hwKwd);
112 
113     // Store "HW" Keyword data.
114     std::variant<std::vector<uint8_t>> val;
115     try
116     {
117         auto result = bus.call(properties);
118         result.read(val);
119     }
120     catch (const sdbusplus::exception::exception& e)
121     {
122         log<level::ERR>("Get HW Keyword read from VINI Failed");
123         throw std::runtime_error("Get HW Keyword read from VINI Failed");
124     }
125 
126     auto hwData = std::get<std::vector<uint8_t>>(val);
127 
128     //"HW" Keyword size is 2 as per VPD spec.
129     constexpr auto hwKwdSize = 2;
130     if (hwKwdSize != hwData.size())
131     {
132         log<level::ERR>(
133             fmt::format("Incorrect VINI records HW Keyword data size({})",
134                         hwData.size())
135                 .c_str());
136         throw std::runtime_error("Incorrect VINI records HW Keyword data size");
137     }
138 
139     log<level::DEBUG>(fmt::format("VINI Records HW[0]:{} HW[1]:{}",
140                                   hwData.at(0), hwData.at(1))
141                           .c_str());
142 
143     // VINI Record "HW" keyword's Byte 0's MSB bit indicates
144     // proc or planar type need to choose.
145     constexpr uint8_t SYS_CLK_NE_TERMINATION_ON_MASK = 0x80;
146 
147     ATTR_SYS_CLK_NE_TERMINATION_SITE_Type clockTerm =
148         ENUM_ATTR_SYS_CLK_NE_TERMINATION_SITE_PLANAR;
149 
150     if (SYS_CLK_NE_TERMINATION_ON_MASK & hwData.at(0))
151     {
152         clockTerm = ENUM_ATTR_SYS_CLK_NE_TERMINATION_SITE_PROC;
153     }
154 
155     // update all the processor attributes
156     struct pdbg_target* procTarget;
157     pdbg_for_each_class_target("proc", procTarget)
158     {
159 
160         if (DT_SET_PROP(ATTR_SYS_CLK_NE_TERMINATION_SITE, procTarget,
161                         clockTerm))
162         {
163             log<level::ERR>(
164                 "Attribute ATTR_SYS_CLK_NE_TERMINATION_SITE set failed");
165             throw std::runtime_error(
166                 "Attribute ATTR_SYS_CLK_NE_TERMINATION_SITE set failed");
167         }
168     }
169 }
170 
171 /**
172  * @brief Starts the self boot engine on POWER processor position 0
173  *        to kick off a boot.
174  * @return void
175  */
176 void startHost(enum ipl_type iplType = IPL_TYPE_NORMAL)
177 {
178     try
179     {
180         phal_init();
181         ipl_set_type(iplType);
182         if (iplType == IPL_TYPE_NORMAL)
183         {
184             // Update SEEPROM side only for NORMAL boot
185             selectBootSeeprom();
186         }
187         setClkNETerminationSite();
188     }
189     catch (const std::exception& ex)
190     {
191         log<level::ERR>("Exception raised during ipl initialisation",
192                         entry("EXCEPTION=%s", ex.what()));
193         openpower::pel::createPEL("org.open_power.PHAL.Error.Boot");
194         openpower::pel::detail::processBootError(false);
195         throw std::runtime_error("IPL initialization failed");
196     }
197 
198     // To clear trace if success
199     openpower::pel::detail::processBootError(true);
200 
201     // callback method will be called upon failure which will create the PEL
202     int rc = ipl_run_major(0);
203     if (rc > 0)
204     {
205         log<level::ERR>("step 0 failed to start the host");
206         throw std::runtime_error("Failed to execute host start boot step");
207     }
208 }
209 
210 /**
211  * @brief Starts the reboot with type memory preserving reboot.
212  * @return void
213  */
214 void startHostMpReboot()
215 {
216     // set ipl type as mpipl
217     startHost(IPL_TYPE_MPIPL);
218 }
219 
220 /**
221  * @brief Starts the normal boot type.
222  * @return void
223  */
224 void startHostNormal()
225 {
226     startHost(IPL_TYPE_NORMAL);
227 }
228 
229 REGISTER_PROCEDURE("startHost", startHostNormal)
230 REGISTER_PROCEDURE("startHostMpReboot", startHostMpReboot)
231 
232 } // namespace phal
233 } // namespace openpower
234