diff options
author | Jaghathiswari Rankappagounder Natarajan <jaghu@google.com> | 2019-02-12 13:22:55 -0800 |
---|---|---|
committer | Jaghathiswari Rankappagounder Natarajan <jaghu@google.com> | 2019-02-15 21:48:56 +0000 |
commit | 9c11894eb919034fb7841520aea3f476783a85c6 (patch) | |
tree | 7d3b244ea082418317608fad7877de59bb549bb1 | |
parent | 0780df105bdd00184de29f48e9946bebfc5b5fb2 (diff) | |
download | phosphor-host-ipmid-9c11894eb919034fb7841520aea3f476783a85c6.tar.gz phosphor-host-ipmid-9c11894eb919034fb7841520aea3f476783a85c6.zip |
Add support for Entity Association Record
Adding support for Entity Association Record (SDR type - 0x08h)
This patch includes:
1) Entity Association Record yaml file example
2) Entity Assocation Record related script and mako file changes
3) Adding Entity Association Record in get_sdr IPMI command response
From the host, tested that entity association records can be fetched
Change-Id: I9cf598e5d27d2e8c6751bbaae2176e7c976974b1
Tested: Yes
Signed-off-by: Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rwxr-xr-x | scripts/entity-example.yaml | 127 | ||||
-rwxr-xr-x | scripts/entity_gen.py | 60 | ||||
-rw-r--r-- | scripts/writeentity.mako.cpp | 34 | ||||
-rw-r--r-- | sensorhandler.cpp | 94 | ||||
-rw-r--r-- | sensorhandler.hpp | 54 | ||||
-rw-r--r-- | types.hpp | 15 |
8 files changed, 387 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am index 9bf6d4e..20af6ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,8 @@ nodist_ipmid_SOURCES = ipmiwhitelist.cpp libipmi20_BUILT_LIST = \ sensor-gen.cpp \ inventory-sensor-gen.cpp \ - fru-read-gen.cpp + fru-read-gen.cpp \ + entity-gen.cpp BUILT_SOURCES = \ ipmiwhitelist.cpp \ @@ -70,6 +71,9 @@ inventory-sensor-gen.cpp: fru-read-gen.cpp: $(AM_V_GEN)@FRUGEN@ -o $(top_builddir) generate-cpp +entity-gen.cpp: + $(AM_V_GEN)@ENTITYGEN@ -o $(top_builddir) generate-cpp + providers_LTLIBRARIES += libipmi20.la libipmi20_la_SOURCES = \ app/channel.cpp \ diff --git a/configure.ac b/configure.ac index 8ad85f2..471a7be 100644 --- a/configure.ac +++ b/configure.ac @@ -97,6 +97,10 @@ AS_IF([test "x$FRU_YAML_GEN" == "x"], [FRU_YAML_GEN="fru-read-example.yaml"]) FRUGEN="$PYTHON $srcdir/scripts/fru_gen.py -i $FRU_YAML_GEN" AC_SUBST(FRUGEN) +AS_IF([test "x$ENTITY_YAML_GEN" == "x"], [ENTITY_YAML_GEN="entity-example.yaml"]) +ENTITYGEN="$PYTHON $srcdir/scripts/entity_gen.py -i $ENTITY_YAML_GEN" +AC_SUBST(ENTITYGEN) + AC_DEFINE(CALLOUT_FWD_ASSOCIATION, "callout", [The name of the callout's forward association.]) AC_DEFINE(BOARD_SENSOR, "/xyz/openbmc_project/inventory/system/chassis/motherboard", [The inventory path to the motherboard fault sensor.]) AC_DEFINE(SYSTEM_SENSOR, "/xyz/openbmc_project/inventory/system", [The inventory path to the system event sensor.]) diff --git a/scripts/entity-example.yaml b/scripts/entity-example.yaml new file mode 100755 index 0000000..2db14cc --- /dev/null +++ b/scripts/entity-example.yaml @@ -0,0 +1,127 @@ +# This record has: +# Container Entity Id and Container Entity Instance = (0x13, 0x81) +# Contained Entity Id and Contained Entity Instance = (0x0A, 0x1), +# (0x0A, 0x3), (0x0A, 0x5), (0x0A, 0x7) +# Entity Record id is the key +0x01: + # Container entity contains other entities + # Entity Id and entity Instance for the container entity + containerEntityId: 0x13 + containerEntityInstance: 0x81 + # A record can have contained entities as a four entry list or as upto + # two ranges of entity instances; this record has contained entities + # as a four entry list + isList: "true" + # Records can be linked if necessary to extend the number of contained + # entities within a container entity; this record is not linked + isLinked: "false" + entityId1: 0x0A + entityInstance1: 0x1 + entityId2: 0x0A + entityInstance2: 0x3 + entityId3: 0x0A + entityInstance3: 0x5 + entityId4: 0x0A + entityInstance4: 0x7 + +# The below two records have: +# Container Entity Id and Container Entity Instance = (0x18, 0x2) +# Contained Entity Id and Contained Entity Instance = (0x1D, 0x1), +# (0x1D, 0x4), (0x1D, 0x6), (0x2B, 0x1), (0x2B, 0x3), (0x0F, 0x1), +# (0x0F, 0x3), (0x10, 0x5) +0x02: + containerEntityId: 0x18 + containerEntityInstance: 0x2 + # This record has contained entities as a four entry list + isList: "true" + # This record is linked with the below record; this record and the + # below record have the same container entity Id and container entity + # instance; + isLinked: "true" + entityId1: 0x1D + entityInstance1: 0x1 + entityId2: 0x1D + entityInstance2: 0x4 + entityId3: 0x1D + entityInstance3: 0x6 + entityId4: 0x2B + entityInstance4: 0x1 + +0x03: + containerEntityId: 0x18 + containerEntityInstance: 0x2 + # This record has contained entities as a four entry list + isList: "true" + # This record is linked with the above record; this record and the + # above record have the same container entity Id and container entity + # instance + isLinked: "true" + entityId1: 0x2B + entityInstance1: 0x3 + entityId2: 0x0F + entityInstance2: 0x1 + entityId3: 0x0F + entityInstance3: 0x3 + entityId4: 0x10 + entityInstance4: 0x5 + +# This record has: +# Container Entity Id and Container Entity Instance = (0x1E, 0x1) +# Contained Entity Id and Contained Entity Instance = (0x20, 0x1), +# (0x20, 0x2), (0x20, 0x3), (0x20, 0x7), (0x20, 0x8), (0x20, 0x9) +0x04: + containerEntityId: 0x1E + containerEntityInstance: 0x1 + # This record has contained entities as two ranges of entity instances + isList: "false" + # This record is not linked + isLinked: "false" + entityId1: 0x20 + entityInstance1: 0x1 + entityId2: 0x20 + entityInstance2: 0x3 + entityId3: 0x20 + entityInstance3: 0x7 + entityId4: 0x20 + entityInstance4: 0x9 + +# The below two records have: +# Container Entity Id and Container Entity Instance = (0x1E, 0x3) +# Contained Entity Id and Contained Entity Instance = (0x20, 0x1), +# (0x20, 0x2), (0x20, 0x3), (0x20, 0x6), (0x20, 0x7), (0x20, 0x8), +# (0x20, 0xA), (0x20, 0xB), (0x20, 0xD), (0x20, 0xE), (0x20, 0xF) +0x05: + containerEntityId: 0x1E + containerEntityInstance: 0x03 + # This record has contained entities as two ranges of entity instances + isList: "false" + # This record is linked with the below record; this record and the + # below record have the same container entity Id and container entity + # instance; + isLinked: "true" + entityId1: 0x20 + entityInstance1: 0x1 + entityId2: 0x20 + entityInstance2: 0x3 + entityId3: 0x20 + entityInstance3: 0x6 + entityId4: 0x20 + entityInstance4: 0x8 + +0x06: + containerEntityId: 0x1E + containerEntityInstance: 0x03 + # This record has contained entities as two ranges of entity instances + isList: "false" + # This record is linked with the above record; this record and the + # above record have the same container entity Id and container entity + # instance; + isLinked: "true" + entityId1: 0x20 + entityInstance1: 0xA + entityId2: 0x20 + entityInstance2: 0xB + entityId3: 0x20 + entityInstance3: 0xD + entityId4: 0x20 + entityInstance4: 0xF diff --git a/scripts/entity_gen.py b/scripts/entity_gen.py new file mode 100755 index 0000000..057821b --- /dev/null +++ b/scripts/entity_gen.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +import os +import sys +import yaml +import argparse +from mako.template import Template + + +def generate_cpp(entity_yaml, output_dir): + with open(os.path.join(script_dir, entity_yaml), 'r') as f: + ifile = yaml.safe_load(f) + if not isinstance(ifile, dict): + ifile = {} + + # Render the mako template + + t = Template(filename=os.path.join( + script_dir, + "writeentity.mako.cpp")) + + output_cpp = os.path.join(output_dir, "entity-gen.cpp") + with open(output_cpp, 'w') as fd: + fd.write(t.render(entityDict=ifile)) + + +def main(): + + valid_commands = { + 'generate-cpp': generate_cpp + } + parser = argparse.ArgumentParser( + description="IPMI Entity record parser and code generator") + + parser.add_argument( + '-i', '--entity_yaml', dest='entity_yaml', + default='example.yaml', help='input entity yaml file to parse') + + parser.add_argument( + "-o", "--output-dir", dest="outputdir", + default=".", + help="output directory") + + parser.add_argument( + 'command', metavar='COMMAND', type=str, + choices=valid_commands.keys(), + help='Command to run.') + + args = parser.parse_args() + + if (not (os.path.isfile(os.path.join(script_dir, args.entity_yaml)))): + sys.exit("Can not find input yaml file " + args.entity_yaml) + + function = valid_commands[args.command] + function(args.entity_yaml, args.outputdir) + + +if __name__ == '__main__': + script_dir = os.path.dirname(os.path.realpath(__file__)) + main() diff --git a/scripts/writeentity.mako.cpp b/scripts/writeentity.mako.cpp new file mode 100644 index 0000000..9de11a0 --- /dev/null +++ b/scripts/writeentity.mako.cpp @@ -0,0 +1,34 @@ +## This file is a template. The comment below is emitted +## into the rendered file; feel free to edit this file. +// !!! WARNING: This is a GENERATED Code..Please do NOT Edit !!! + +#include "types.hpp" +using namespace ipmi::sensor; + +extern const EntityInfoMap entities = { +% for key in entityDict.iterkeys(): +{${key},{ +<% + entity = entityDict[key] + containerEntityId = entity["containerEntityId"] + containerEntityInstance = entity["containerEntityInstance"] + isList = entity["isList"] + isLinked = entity["isLinked"] + entityId1 = entity["entityId1"] + entityInstance1 = entity["entityInstance1"] + entityId2 = entity["entityId2"] + entityInstance2 = entity["entityInstance2"] + entityId3 = entity["entityId3"] + entityInstance3 = entity["entityInstance3"] + entityId4 = entity["entityId4"] + entityInstance4 = entity["entityInstance4"] +%> + ${containerEntityId},${containerEntityInstance},${isList},${isLinked},{ + std::make_pair(${entityId1}, ${entityInstance1}), + std::make_pair(${entityId2}, ${entityInstance2}), + std::make_pair(${entityId3}, ${entityInstance3}), + std::make_pair(${entityId4}, ${entityInstance4}) } + +}}, +% endfor +}; diff --git a/sensorhandler.cpp b/sensorhandler.cpp index 40c0d3e..9d0364d 100644 --- a/sensorhandler.cpp +++ b/sensorhandler.cpp @@ -27,6 +27,7 @@ extern int updateSensorRecordFromSSRAESC(const void*); extern sd_bus* bus; extern const ipmi::sensor::IdInfoMap sensors; extern const FruMap frus; +extern const ipmi::sensor::EntityInfoMap entities; using namespace phosphor::logging; using InternalFailure = @@ -575,7 +576,7 @@ ipmi_ret_t ipmi_sen_get_sdr_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, get_sdr_info::request::get_count(request) == false) { // Get Sensor Count - resp->count = sensors.size() + frus.size(); + resp->count = sensors.size() + frus.size() + entities.size(); } else { @@ -748,13 +749,87 @@ ipmi_ret_t ipmi_fru_get_sdr(ipmi_request_t request, ipmi_response_t response, if (++fru == frus.end()) { + // we have reached till end of fru, so assign the next record id to + // 512(Max fru ID = 511) + Entity Record ID(may start with 0). + auto next_record_id = + (entities.size()) ? entities.begin()->first + ENTITY_RECORD_ID_START + : END_OF_RECORD; + get_sdr::response::set_next_record_id(next_record_id, resp); + } + else + { + get_sdr::response::set_next_record_id( + (FRU_RECORD_ID_START + fru->first), resp); + } + + // Check for invalid offset size + if (req->offset > sizeof(record)) + { + return IPMI_CC_PARM_OUT_OF_RANGE; + } + + dataLength = std::min(static_cast<size_t>(req->bytes_to_read), + sizeof(record) - req->offset); + + std::memcpy(resp->record_data, + reinterpret_cast<uint8_t*>(&record) + req->offset, dataLength); + + *data_len = dataLength; + *data_len += 2; // additional 2 bytes for next record ID + + return IPMI_CC_OK; +} + +ipmi_ret_t ipmi_entity_get_sdr(ipmi_request_t request, ipmi_response_t response, + ipmi_data_len_t data_len) +{ + auto req = reinterpret_cast<get_sdr::GetSdrReq*>(request); + auto resp = reinterpret_cast<get_sdr::GetSdrResp*>(response); + get_sdr::SensorDataEntityRecord record{}; + auto dataLength = 0; + + auto entity = entities.begin(); + uint8_t entityRecordID; + auto recordID = get_sdr::request::get_record_id(req); + + entityRecordID = recordID - ENTITY_RECORD_ID_START; + entity = entities.find(entityRecordID); + if (entity == entities.end()) + { + return IPMI_CC_SENSOR_INVALID; + } + + /* Header */ + get_sdr::header::set_record_id(recordID, &(record.header)); + record.header.sdr_version = SDR_VERSION; // Based on IPMI Spec v2.0 rev 1.1 + record.header.record_type = get_sdr::SENSOR_DATA_ENTITY_RECORD; + record.header.record_length = sizeof(record.key) + sizeof(record.body); + + /* Key */ + record.key.containerEntityId = entity->second.containerEntityId; + record.key.containerEntityInstance = entity->second.containerEntityInstance; + get_sdr::key::set_flags(entity->second.isList, entity->second.isLinked, + &(record.key)); + record.key.entityId1 = entity->second.containedEntities[0].first; + record.key.entityInstance1 = entity->second.containedEntities[0].second; + + /* Body */ + record.body.entityId2 = entity->second.containedEntities[1].first; + record.body.entityInstance2 = entity->second.containedEntities[1].second; + record.body.entityId3 = entity->second.containedEntities[2].first; + record.body.entityInstance3 = entity->second.containedEntities[2].second; + record.body.entityId4 = entity->second.containedEntities[3].first; + record.body.entityInstance4 = entity->second.containedEntities[3].second; + + if (++entity == entities.end()) + { get_sdr::response::set_next_record_id(END_OF_RECORD, resp); // last record } else { get_sdr::response::set_next_record_id( - (FRU_RECORD_ID_START + fru->first), resp); + (ENTITY_RECORD_ID_START + entity->first), resp); } // Check for invalid offset size @@ -793,10 +868,17 @@ ipmi_ret_t ipmi_sen_get_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd, // At the beginning of a scan, the host side will send us id=0. if (recordID != 0) { - // recordID greater then 255,it means it is a FRU record. - // Currently we are supporting two record types either FULL record - // or FRU record. - if (recordID >= FRU_RECORD_ID_START) + // recordID 0 to 255 means it is a FULL record. + // recordID 256 to 511 means it is a FRU record. + // recordID greater then 511 means it is a Entity Association + // record. Currently we are supporting three record types: FULL + // record, FRU record and Enttiy Association record. + if (recordID >= ENTITY_RECORD_ID_START) + { + return ipmi_entity_get_sdr(request, response, data_len); + } + else if (recordID >= FRU_RECORD_ID_START && + recordID < ENTITY_RECORD_ID_START) { return ipmi_fru_get_sdr(request, response, data_len); } diff --git a/sensorhandler.hpp b/sensorhandler.hpp index 0338597..8fcf2b1 100644 --- a/sensorhandler.hpp +++ b/sensorhandler.hpp @@ -86,6 +86,7 @@ ipmi_ret_t ipmi_sen_reserve_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_context_t context); static const uint16_t FRU_RECORD_ID_START = 256; +static const uint16_t ENTITY_RECORD_ID_START = 512; static const uint8_t SDR_VERSION = 0x51; static const uint16_t END_OF_RECORD = 0xFFFF; static const uint8_t LENGTH_MASK = 0x1F; @@ -210,6 +211,7 @@ enum SensorDataRecordType { SENSOR_DATA_FULL_RECORD = 0x1, SENSOR_DATA_FRU_RECORD = 0x11, + SENSOR_DATA_ENTITY_RECORD = 0x8, }; // Record key @@ -232,9 +234,25 @@ struct SensorDataFruRecordKey uint8_t channelNumber; } __attribute__((packed)); +/** @struct SensorDataEntityRecordKey + * + * Entity Association Record(key) - SDR Type 8 + */ +struct SensorDataEntityRecordKey +{ + uint8_t containerEntityId; + uint8_t containerEntityInstance; + uint8_t flags; + uint8_t entityId1; + uint8_t entityInstance1; +} __attribute__((packed)); + namespace key { +static constexpr uint8_t listOrRangeBit = 7; +static constexpr uint8_t linkedBit = 6; + inline void set_owner_id_ipmb(SensorDataRecordKey* key) { key->owner_id &= ~0x01; @@ -268,6 +286,17 @@ inline void set_owner_lun_channel(uint8_t channel, SensorDataRecordKey* key) key->owner_lun |= ((channel & 0xf) << 4); }; +inline void set_flags(bool isList, bool isLinked, + SensorDataEntityRecordKey* key) +{ + key->flags = 0x00; + if (!isList) + key->flags |= 1 << listOrRangeBit; + + if (isLinked) + key->flags |= 1 << linkedBit; +}; + } // namespace key /** @struct GetSensorThresholdsResponse @@ -347,6 +376,20 @@ struct SensorDataFruRecordBody char deviceID[FRU_RECORD_DEVICE_ID_MAX_LENGTH]; } __attribute__((packed)); +/** @struct SensorDataEntityRecordBody + * + * Entity Association Record(body) - SDR Type 8 + */ +struct SensorDataEntityRecordBody +{ + uint8_t entityId2; + uint8_t entityInstance2; + uint8_t entityId3; + uint8_t entityInstance3; + uint8_t entityId4; + uint8_t entityInstance4; +} __attribute__((packed)); + namespace body { @@ -594,6 +637,17 @@ struct SensorDataFruRecord SensorDataFruRecordBody body; } __attribute__((packed)); +/** @struct SensorDataEntityRecord + * + * Entity Association Record - SDR Type 8 + */ +struct SensorDataEntityRecord +{ + SensorDataRecordHeader header; + SensorDataEntityRecordKey key; + SensorDataEntityRecordBody body; +} __attribute__((packed)); + } // namespace get_sdr namespace ipmi @@ -196,6 +196,21 @@ enum class ThresholdMask CRITICAL_HIGH_MASK = 0x10, }; +static constexpr uint8_t maxContainedEntities = 4; +using ContainedEntitiesArray = + std::array<std::pair<uint8_t, uint8_t>, maxContainedEntities>; + +struct EntityInfo +{ + uint8_t containerEntityId; + uint8_t containerEntityInstance; + bool isList; + bool isLinked; + ContainedEntitiesArray containedEntities; +}; + +using EntityInfoMap = std::map<Id, EntityInfo>; + } // namespace sensor namespace network |