xref: /openbmc/openpower-occ-control/occ_ffdc.cpp (revision e2a58518499b01b931e29f12ed0bcfc586452f75)
1 #include "occ_ffdc.hpp"
2 
3 #include "elog-errors.hpp"
4 #include "utils.hpp"
5 
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <fmt/core.h>
9 #include <stdio.h>
10 #include <sys/ioctl.h>
11 #include <unistd.h>
12 
13 #include <org/open_power/OCC/Device/error.hpp>
14 #include <phosphor-logging/elog.hpp>
15 #include <phosphor-logging/log.hpp>
16 #include <xyz/openbmc_project/Common/error.hpp>
17 #include <xyz/openbmc_project/Logging/Create/server.hpp>
18 
19 namespace open_power
20 {
21 namespace occ
22 {
23 
24 static constexpr size_t max_ffdc_size = 8192;
25 static constexpr size_t sbe_status_header_size = 8;
26 
27 static constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
28 static constexpr auto loggingInterface = "xyz.openbmc_project.Logging.Create";
29 static constexpr auto opLoggingInterface = "org.open_power.Logging.PEL";
30 
31 using namespace phosphor::logging;
32 using namespace sdbusplus::org::open_power::OCC::Device::Error;
33 using InternalFailure =
34     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
35 
36 uint32_t FFDC::createPEL(const char* path, uint32_t src6, const char* msg,
37                          int fd)
38 {
39     uint32_t plid = 0;
40     std::vector<std::tuple<
41         sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
42         uint8_t, uint8_t, sdbusplus::message::unix_fd>>
43         pelFFDCInfo;
44 
45     log<level::INFO>("Creating PEL with SBE FFDC", entry("SRC6=%08x", src6));
46 
47     if (fd > 0)
48     {
49         pelFFDCInfo.push_back(std::make_tuple(
50             sdbusplus::xyz::openbmc_project::Logging::server::Create::
51                 FFDCFormat::Custom,
52             static_cast<uint8_t>(0xCB), static_cast<uint8_t>(0x01), fd));
53     }
54 
55     std::map<std::string, std::string> additionalData;
56     additionalData.emplace("SRC6", std::to_string(src6));
57     additionalData.emplace("_PID", std::to_string(getpid()));
58     additionalData.emplace("SBE_ERR_MSG", msg);
59 
60     auto& bus = utils::getBus();
61 
62     try
63     {
64         std::string service =
65             utils::getService(loggingObjectPath, opLoggingInterface);
66         auto method =
67             bus.new_method_call(service.c_str(), loggingObjectPath,
68                                 opLoggingInterface, "CreatePELWithFFDCFiles");
69         auto level =
70             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
71                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
72                     Error);
73         method.append(path, level, additionalData, pelFFDCInfo);
74         auto response = bus.call(method);
75         std::tuple<uint32_t, uint32_t> reply = {0, 0};
76 
77         response.read(reply);
78         plid = std::get<1>(reply);
79     }
80     catch (const sdbusplus::exception_t& e)
81     {
82         log<level::ERR>("Failed to create PEL");
83     }
84 
85     return plid;
86 }
87 
88 void FFDC::createOCCResetPEL(unsigned int instance, const char* path, int err,
89                              const char* callout)
90 {
91     std::map<std::string, std::string> additionalData;
92 
93     additionalData.emplace("_PID", std::to_string(getpid()));
94 
95     if (err)
96     {
97         additionalData.emplace("CALLOUT_ERRNO", std::to_string(-err));
98     }
99 
100     if (callout)
101     {
102         additionalData.emplace("CALLOUT_DEVICE_PATH", std::string(callout));
103     }
104 
105     additionalData.emplace("OCC", std::to_string(instance));
106 
107     auto& bus = utils::getBus();
108 
109     try
110     {
111         std::string service =
112             utils::getService(loggingObjectPath, loggingInterface);
113         auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
114                                           loggingInterface, "Create");
115         auto level =
116             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
117                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
118                     Error);
119         method.append(path, level, additionalData);
120         bus.call(method);
121     }
122     catch (const sdbusplus::exception_t& e)
123     {
124         log<level::ERR>(
125             fmt::format("Failed to create PEL: {}", e.what()).c_str());
126     }
127 }
128 
129 // Reads the FFDC file and create an error log
130 void FFDC::analyzeEvent()
131 {
132     int tfd = -1;
133     size_t total = 0;
134     auto data = std::make_unique<unsigned char[]>(max_ffdc_size);
135     while (total < max_ffdc_size)
136     {
137         auto r = read(fd, data.get() + total, max_ffdc_size - total);
138         if (r < 0)
139         {
140             elog<ReadFailure>(
141                 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
142                     CALLOUT_ERRNO(errno),
143                 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
144                     CALLOUT_DEVICE_PATH(file.c_str()));
145             return;
146         }
147         if (!r)
148         {
149             break;
150         }
151         total += r;
152     }
153 
154     lseek(fd, 0, SEEK_SET);
155 
156     if (!total)
157     {
158         // no error
159         return;
160     }
161 
162     uint32_t src6 = instance << 16;
163     src6 |= *(data.get() + 2) << 8;
164     src6 |= *(data.get() + 3);
165 
166     if (total > sbe_status_header_size)
167     {
168         std::string templateString =
169             fs::temp_directory_path() / "OCC_FFDC_XXXXXX";
170         tfd = mkostemp(templateString.data(), O_RDWR);
171         if (tfd < 0)
172         {
173             log<level::ERR>("Couldn't create temporary FFDC file");
174         }
175         else
176         {
177             temporaryFiles.emplace_back(templateString, tfd);
178             size_t written = sbe_status_header_size;
179             while (written < total)
180             {
181                 auto r = write(tfd, data.get() + written, total - written);
182                 if (r < 0)
183                 {
184                     close(temporaryFiles.back().second);
185                     fs::remove(temporaryFiles.back().first);
186                     temporaryFiles.pop_back();
187                     tfd = -1;
188                     log<level::ERR>("Couldn't write temporary FFDC file");
189                     break;
190                 }
191                 if (!r)
192                 {
193                     break;
194                 }
195                 written += r;
196             }
197         }
198     }
199 
200     createPEL("org.open_power.Processor.Error.SbeChipOpFailure", src6,
201               "SBE command reported error", tfd);
202 }
203 
204 } // namespace occ
205 } // namespace open_power
206