/* // Copyright (c) 2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include #include #include #include #include #include #include namespace intel_oem::ipmi::sel { namespace redfish_hooks { static void toHexStr(const boost::beast::span bytes, std::string& hexStr) { std::stringstream stream; stream << std::hex << std::uppercase << std::setfill('0'); for (const uint8_t& byte : bytes) { stream << std::setw(2) << static_cast(byte); } hexStr = stream.str(); } // Record a BIOS message as a Redfish message instead of a SEL record static bool biosMessageHook(const SELData& selData, const std::string& ipmiRaw) { // This is a BIOS message, so record it as a Redfish message instead // of a SEL record // Walk through the SEL request record to build the appropriate Redfish // message static constexpr std::string_view openBMCMessageRegistryVersion = "0.1"; std::string messageID = "OpenBMC." + std::string(openBMCMessageRegistryVersion); std::vector messageArgs; BIOSSensors sensor = static_cast(selData.sensorNum); BIOSEventTypes eventType = static_cast(selData.eventType); switch (sensor) { case BIOSSensors::memoryRASConfigStatus: switch (eventType) { case BIOSEventTypes::digitalDiscrete: { switch (selData.offset) { case 0x00: messageID += ".MemoryRASConfigurationDisabled"; break; case 0x01: messageID += ".MemoryRASConfigurationEnabled"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // error = eventData2 bits [3:0] int error = selData.eventData2 & 0x0F; // mode = eventData3 bits [3:0] int mode = selData.eventData3 & 0x0F; // Save the messageArgs switch (error) { case 0x00: messageArgs.push_back("None"); break; case 0x03: messageArgs.push_back("Invalid DIMM Config"); break; default: messageArgs.push_back(std::to_string(error)); break; } switch (mode) { case 0x00: messageArgs.push_back("None"); break; case 0x01: messageArgs.push_back("Mirroring"); break; case 0x02: messageArgs.push_back("Lockstep"); break; case 0x04: messageArgs.push_back("Rank Sparing"); break; default: messageArgs.push_back(std::to_string(mode)); break; } break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSensors::biosPOSTError: switch (eventType) { case BIOSEventTypes::sensorSpecificOffset: { switch (selData.offset) { case 0x00: messageID += ".BIOSPOSTError"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 std::array post; // post LSB = eventData2 bits [7:0] post[1] = selData.eventData2; // post MSB = eventData3 bits [7:0] post[0] = selData.eventData3; // Save the messageArgs messageArgs.emplace_back(); std::string& postStr = messageArgs.back(); toHexStr(boost::beast::span(post), postStr); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSensors::intelUPILinkWidthReduced: switch (eventType) { case BIOSEventTypes::oemDiscrete7: { switch (selData.offset) { case 0x01: messageID += ".IntelUPILinkWidthReducedToHalf"; break; case 0x02: messageID += ".IntelUPILinkWidthReducedToQuarter"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 // Node ID = eventData2 bits [7:0] int node = selData.eventData2; // Save the messageArgs messageArgs.push_back(std::to_string(node + 1)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSensors::memoryRASModeSelect: switch (eventType) { case BIOSEventTypes::digitalDiscrete: { switch (selData.offset) { case 0x00: messageID += ".MemoryRASModeDisabled"; break; case 0x01: messageID += ".MemoryRASModeEnabled"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // prior mode = eventData2 bits [3:0] int priorMode = selData.eventData2 & 0x0F; // selected mode = eventData3 bits [3:0] int selectedMode = selData.eventData3 & 0x0F; // Save the messageArgs switch (priorMode) { case 0x00: messageArgs.push_back("None"); break; case 0x01: messageArgs.push_back("Mirroring"); break; case 0x02: messageArgs.push_back("Lockstep"); break; case 0x04: messageArgs.push_back("Rank Sparing"); break; default: messageArgs.push_back(std::to_string(priorMode)); break; } switch (selectedMode) { case 0x00: messageArgs.push_back("None"); break; case 0x01: messageArgs.push_back("Mirroring"); break; case 0x02: messageArgs.push_back("Lockstep"); break; case 0x04: messageArgs.push_back("Rank Sparing"); break; default: messageArgs.push_back(std::to_string(selectedMode)); break; } break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSensors::bootEvent: switch (eventType) { case BIOSEventTypes::sensorSpecificOffset: { switch (selData.offset) { case 0x01: messageID += ".BIOSBoot"; break; case 0x09: messageID += ".BIOSAttributesChanged"; break; default: return defaultMessageHook(ipmiRaw); break; } break; } default: return defaultMessageHook(ipmiRaw); break; } break; default: return defaultMessageHook(ipmiRaw); break; } // Log the Redfish message to the journal with the appropriate metadata std::string journalMsg = "BIOS POST IPMI event: " + ipmiRaw; if (messageArgs.empty()) { phosphor::logging::log( journalMsg.c_str(), phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageID.c_str())); } else { std::string messageArgsString = boost::algorithm::join(messageArgs, ","); phosphor::logging::log( journalMsg.c_str(), phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageID.c_str()), phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s", messageArgsString.c_str())); } return true; } // Record a BIOS SMI message as a Redfish message instead of a SEL record static bool biosSMIMessageHook(const SELData& selData, const std::string& ipmiRaw) { // This is a BIOS SMI message, so record it as a Redfish message instead // of a SEL record // Walk through the SEL request record to build the appropriate Redfish // message static constexpr std::string_view openBMCMessageRegistryVersion = "0.1"; std::string messageID = "OpenBMC." + std::string(openBMCMessageRegistryVersion); std::vector messageArgs; BIOSSMISensors sensor = static_cast(selData.sensorNum); BIOSEventTypes eventType = static_cast(selData.eventType); switch (sensor) { case BIOSSMISensors::mirroringRedundancyState: switch (eventType) { case BIOSEventTypes::discreteRedundancyStates: { switch (selData.offset) { case 0x00: messageID += ".MirroringRedundancyFull"; break; case 0x02: messageID += ".MirroringRedundancyDegraded"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // pair = eventData2 bits [7:4] int pair = selData.eventData2 >> 4 & 0x0F; // rank = eventData2 bits [1:0] int rank = selData.eventData2 & 0x03; // Socket ID = eventData3 bits [7:5] int socket = selData.eventData3 >> 5 & 0x07; // Channel = eventData3 bits [4:2] int channel = selData.eventData3 >> 2 & 0x07; char channelLetter[4] = {'A'}; channelLetter[0] += channel; // DIMM = eventData3 bits [1:0] int dimm = selData.eventData3 & 0x03; // Save the messageArgs messageArgs.push_back(std::to_string(socket + 1)); messageArgs.push_back(std::string(channelLetter)); messageArgs.push_back(std::to_string(dimm + 1)); messageArgs.push_back(std::to_string(pair)); messageArgs.push_back(std::to_string(rank)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::memoryECCError: switch (eventType) { case BIOSEventTypes::sensorSpecificOffset: { switch (selData.offset) { case 0x00: messageID += ".MemoryECCCorrectable"; break; case 0x01: messageID += ".MemoryECCUncorrectable"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // dimm = eventData2 bits [7:4] int dimm = selData.eventData2 >> 4 & 0x0F; // rank = eventData2 bits [3:0] int rank = selData.eventData2 & 0x0F; // Socket ID = eventData3 bits [7:4] int socket = selData.eventData3 >> 4 & 0x0F; // Channel = eventData3 bits [3:0] int channel = selData.eventData3 & 0x0F; char channelLetter[4] = {'A'}; channelLetter[0] += channel; // Save the messageArgs messageArgs.push_back(std::to_string(socket + 1)); messageArgs.push_back(std::string(channelLetter)); messageArgs.push_back(std::to_string(dimm)); messageArgs.push_back(std::to_string(rank)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::legacyPCIError: switch (eventType) { case BIOSEventTypes::sensorSpecificOffset: { switch (selData.offset) { case 0x04: messageID += ".LegacyPCIPERR"; break; case 0x05: messageID += ".LegacyPCISERR"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // Bus = eventData2 bits [7:0] int bus = selData.eventData2; // Device = eventData3 bits [7:3] int device = selData.eventData3 >> 3 & 0x1F; // Function = eventData3 bits [2:0] int function = selData.eventData3 >> 0x07; // Save the messageArgs messageArgs.push_back(std::to_string(bus)); messageArgs.push_back(std::to_string(device)); messageArgs.push_back(std::to_string(function)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::pcieFatalError: switch (eventType) { case BIOSEventTypes::oemDiscrete0: { switch (selData.offset) { case 0x00: messageID += ".PCIeFatalDataLinkLayerProtocol"; break; case 0x01: messageID += ".PCIeFatalSurpriseLinkDown"; break; case 0x02: messageID += ".PCIeFatalCompleterAbort"; break; case 0x03: messageID += ".PCIeFatalUnsupportedRequest"; break; case 0x04: messageID += ".PCIeFatalPoisonedTLP"; break; case 0x05: messageID += ".PCIeFatalFlowControlProtocol"; break; case 0x06: messageID += ".PCIeFatalCompletionTimeout"; break; case 0x07: messageID += ".PCIeFatalReceiverBufferOverflow"; break; case 0x08: messageID += ".PCIeFatalACSViolation"; break; case 0x09: messageID += ".PCIeFatalMalformedTLP"; break; case 0x0a: messageID += ".PCIeFatalECRCError"; break; case 0x0b: messageID += ".PCIeFatalReceivedFatalMessageFromDownstream"; break; case 0x0c: messageID += ".PCIeFatalUnexpectedCompletion"; break; case 0x0d: messageID += ".PCIeFatalReceivedErrNonFatalMessage"; break; case 0x0e: messageID += ".PCIeFatalUncorrectableInternal"; break; case 0x0f: messageID += ".PCIeFatalMCBlockedTLP"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // Bus = eventData2 bits [7:0] int bus = selData.eventData2; // Device = eventData3 bits [7:3] int device = selData.eventData3 >> 3 & 0x1F; // Function = eventData3 bits [2:0] int function = selData.eventData3 >> 0x07; // Save the messageArgs messageArgs.push_back(std::to_string(bus)); messageArgs.push_back(std::to_string(device)); messageArgs.push_back(std::to_string(function)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::pcieCorrectableError: switch (eventType) { case BIOSEventTypes::oemDiscrete1: { switch (selData.offset) { case 0x00: messageID += ".PCIeCorrectableReceiverError"; break; case 0x01: messageID += ".PCIeCorrectableBadDLLP"; break; case 0x02: messageID += ".PCIeCorrectableBadTLP"; break; case 0x03: messageID += ".PCIeCorrectableReplayNumRollover"; break; case 0x04: messageID += ".PCIeCorrectableReplayTimerTimeout"; break; case 0x05: messageID += ".PCIeCorrectableAdvisoryNonFatal"; break; case 0x06: messageID += ".PCIeCorrectableLinkBWChanged"; break; case 0x07: messageID += ".PCIeCorrectableInternal"; break; case 0x08: messageID += ".PCIeCorrectableHeaderLogOverflow"; break; case 0x0f: messageID += ".PCIeCorrectableUnspecifiedAERError"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // Bus = eventData2 bits [7:0] int bus = selData.eventData2; // Device = eventData3 bits [7:3] int device = selData.eventData3 >> 3 & 0x1F; // Function = eventData3 bits [2:0] int function = selData.eventData3 >> 0x07; // Save the messageArgs messageArgs.push_back(std::to_string(bus)); messageArgs.push_back(std::to_string(device)); messageArgs.push_back(std::to_string(function)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::sparingRedundancyState: switch (eventType) { case BIOSEventTypes::discreteRedundancyStates: { switch (selData.offset) { case 0x00: messageID += ".SparingRedundancyFull"; break; case 0x02: messageID += ".SparingRedundancyDegraded"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // domain = eventData2 bits [7:4] int domain = selData.eventData2 >> 4 & 0x0F; char domainLetter[4] = {'A'}; domainLetter[0] += domain; // rank = eventData2 bits [1:0] int rank = selData.eventData2 & 0x03; // Socket ID = eventData3 bits [7:5] int socket = selData.eventData3 >> 5 & 0x07; // Channel = eventData3 bits [4:2] int channel = selData.eventData3 >> 2 & 0x07; char channelLetter[4] = {'A'}; channelLetter[0] += channel; // DIMM = eventData3 bits [1:0] int dimm = selData.eventData3 & 0x03; // Save the messageArgs messageArgs.push_back(std::to_string(socket + 1)); messageArgs.push_back(std::string(channelLetter)); messageArgs.push_back(std::to_string(dimm + 1)); messageArgs.push_back(std::string(domainLetter)); messageArgs.push_back(std::to_string(rank)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::memoryParityError: switch (eventType) { case BIOSEventTypes::sensorSpecificOffset: { switch (selData.offset) { case 0x03: { // type = eventData2 bits [2:0] int type = selData.eventData2 & 0x07; switch (type) { case 0x00: messageID += ".MemoryParityNotKnown"; break; case 0x03: messageID += ".MemoryParityCommandAndAddress"; break; default: return defaultMessageHook(ipmiRaw); break; } break; } default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // channelValid = eventData2 bit [4] int channelValid = selData.eventData2 >> 4 & 0x01; // dimmValid = eventData2 bit [3] int dimmValid = selData.eventData2 >> 3 & 0x01; // Socket ID = eventData3 bits [7:5] int socket = selData.eventData3 >> 5 & 0x07; // Channel = eventData3 bits [4:2] int channel = selData.eventData3 >> 2 & 0x07; char channelLetter[4] = {'A'}; channelLetter[0] += channel; // DIMM = eventData3 bits [1:0] int dimm = selData.eventData3 & 0x03; // Save the messageArgs messageArgs.push_back(std::to_string(socket + 1)); messageArgs.push_back(std::string(channelLetter)); messageArgs.push_back(std::to_string(dimm + 1)); messageArgs.push_back(std::to_string(channelValid)); messageArgs.push_back(std::to_string(dimmValid)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::pcieFatalError2: switch (eventType) { case BIOSEventTypes::oemDiscrete6: { switch (selData.offset) { case 0x00: messageID += ".PCIeFatalAtomicEgressBlocked"; break; case 0x01: messageID += ".PCIeFatalTLPPrefixBlocked"; break; case 0x0f: messageID += ".PCIeFatalUnspecifiedNonAERFatalError"; break; default: return defaultMessageHook(ipmiRaw); break; } // Get the message data from eventData2 and eventData3 // Bus = eventData2 bits [7:0] int bus = selData.eventData2; // Device = eventData3 bits [7:3] int device = selData.eventData3 >> 3 & 0x1F; // Function = eventData3 bits [2:0] int function = selData.eventData3 >> 0x07; // Save the messageArgs messageArgs.push_back(std::to_string(bus)); messageArgs.push_back(std::to_string(device)); messageArgs.push_back(std::to_string(function)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::biosRecovery: switch (eventType) { case BIOSEventTypes::oemDiscrete0: { switch (selData.offset) { case 0x01: messageID += ".BIOSRecoveryStart"; break; default: return defaultMessageHook(ipmiRaw); break; } break; } case BIOSEventTypes::reservedF0: { switch (selData.offset) { case 0x01: messageID += ".BIOSRecoveryComplete"; break; default: return defaultMessageHook(ipmiRaw); break; } break; } default: return defaultMessageHook(ipmiRaw); break; } break; case BIOSSMISensors::adddcError: switch (eventType) { case BIOSEventTypes::reservedA0: { messageID += ".ADDDCCorrectable"; // Get the message data from eventData2 and eventData3 // dimm = eventData2 bits [7:4] int dimm = selData.eventData2 >> 4 & 0x0F; // rank = eventData2 bits [3:0] int rank = selData.eventData2 & 0x0F; // Socket ID = eventData3 bits [7:4] int socket = selData.eventData3 >> 4 & 0x0F; // Channel = eventData3 bits [3:0] int channel = selData.eventData3 & 0x0F; char channelLetter[4] = {'A'}; channelLetter[0] += channel; // Save the messageArgs messageArgs.push_back(std::to_string(socket + 1)); messageArgs.push_back(std::string(channelLetter)); messageArgs.push_back(std::to_string(dimm)); messageArgs.push_back(std::to_string(rank)); break; } default: return defaultMessageHook(ipmiRaw); break; } break; default: return defaultMessageHook(ipmiRaw); break; } // Log the Redfish message to the journal with the appropriate metadata std::string journalMsg = "BIOS SMI IPMI event: " + ipmiRaw; std::string messageArgsString = boost::algorithm::join(messageArgs, ","); phosphor::logging::log( journalMsg.c_str(), phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageID.c_str()), phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s", messageArgsString.c_str())); return true; } static bool startRedfishHook(const SELData& selData, const std::string& ipmiRaw) { uint8_t generatorIDLowByte = static_cast(selData.generatorID); // Generator ID is 7 bit and LS Bit contains '1' or '0' depending on the // source. Refer IPMI SPEC, Table 32, SEL Event Records. switch (generatorIDLowByte) { case 0x01: // Check if this message is from the BIOS Generator ID // Let the BIOS hook handle this request return biosMessageHook(selData, ipmiRaw); break; case 0x33: // Check if this message is from the BIOS SMI Generator ID // Let the BIOS SMI hook handle this request return biosSMIMessageHook(selData, ipmiRaw); break; case 0x2C: // Message from Intel ME return me::messageHook(selData, ipmiRaw); break; } // No hooks handled the request, so let it go to default return defaultMessageHook(ipmiRaw); } } // namespace redfish_hooks bool checkRedfishHooks(uint16_t recordID, uint8_t recordType, uint32_t timestamp, uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum, uint8_t eventType, uint8_t eventData1, uint8_t eventData2, uint8_t eventData3) { // Save the raw IPMI string of the request std::string ipmiRaw; std::array selBytes = { static_cast(recordID), static_cast(recordID >> 8), recordType, static_cast(timestamp), static_cast(timestamp >> 8), static_cast(timestamp >> 16), static_cast(timestamp >> 24), static_cast(generatorID), static_cast(generatorID >> 8), evmRev, sensorType, sensorNum, eventType, eventData1, eventData2, eventData3}; redfish_hooks::toHexStr(boost::beast::span(selBytes), ipmiRaw); // First check that this is a system event record type since that // determines the definition of the rest of the data if (recordType != ipmi::sel::systemEvent) { // OEM record type, so let it go to the SEL return redfish_hooks::defaultMessageHook(ipmiRaw); } // Extract the SEL data for the hook redfish_hooks::SELData selData = { .generatorID = generatorID, .sensorNum = sensorNum, .eventType = eventType, .offset = eventData1 & 0x0F, .eventData2 = eventData2, .eventData3 = eventData3}; return redfish_hooks::startRedfishHook(selData, ipmiRaw); } bool checkRedfishHooks(uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum, uint8_t eventType, uint8_t eventData1, uint8_t eventData2, uint8_t eventData3) { // Save the raw IPMI string of the selData std::string ipmiRaw; std::array selBytes = { static_cast(generatorID), static_cast(generatorID >> 8), evmRev, sensorType, sensorNum, eventType, eventData1, eventData2, eventData3}; redfish_hooks::toHexStr(boost::beast::span(selBytes), ipmiRaw); // Extract the SEL data for the hook redfish_hooks::SELData selData = { .generatorID = generatorID, .sensorNum = sensorNum, .eventType = eventType, .offset = eventData1 & 0x0F, .eventData2 = eventData2, .eventData3 = eventData3}; return redfish_hooks::startRedfishHook(selData, ipmiRaw); } } // namespace intel_oem::ipmi::sel