user_layer: Add get/set user payload access.

IPMI Spec reference: Section 24.6, 24.7.
Support is added to get/set user access details for the
unreserved, supported payload types defined by Spec.
SOL is the only unreserved, supported payload currently.
If support is needed for unreserved std/oem payload
types in future, they can be enabled with minor source code
changes to this implementation.

All payload types are packed in a JSON object
"payload_enabled" in ipmi_user.json file.

Tested-by:
1. For user 8 in channel 3, Enable SOL payload.

   // Command - (channel 3 is of LAN channel type)
   ipmitool  -I lanplus...raw 0x06 0x4C 3 0x8 0x02 0 0 0
   // Verify it with Get User Payload Access Command
   ipmitool  -I lanplus...raw 0x06 0x4D 3 8
   02 00 00 00 // Response

2. Disable SOL payload.

   // Command
   ipmitool  -I lanplus...raw 0x06 0x4C 3 0x48 0x02 0 0x00 0
   // Verify it with Get User Payload Access Command
   ipmitool  -I lanplus...raw 0x06 0x4D 3 8
   00 00 00 00 // Response

3. Enable unsupported payload stdPayload7.

   // Command
   ipmitool  -I lanplus...raw 0x06 0x4C 3 0x8 0x80 0 0 0
   Error: Invalid data field in request // Response

Change-Id: Idc57b04a747e55666407d928d8b2169223501e5b
Signed-off-by: Saravanan Palanisamy <saravanan.palanisamy@linux.intel.com>
diff --git a/user_channel/user_layer.cpp b/user_channel/user_layer.cpp
index 00f6a7f..b309e86 100644
--- a/user_channel/user_layer.cpp
+++ b/user_channel/user_layer.cpp
@@ -176,4 +176,51 @@
     return pamUserCheckAuthenticate(userName, userPassword);
 }
 
+ipmi_ret_t ipmiUserSetUserPayloadAccess(const uint8_t chNum,
+                                        const uint8_t operation,
+                                        const uint8_t userId,
+                                        const PayloadAccess& payloadAccess)
+{
+
+    if (!UserAccess::isValidChannel(chNum))
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    if (!UserAccess::isValidUserId(userId))
+    {
+        return IPMI_CC_PARM_OUT_OF_RANGE;
+    }
+
+    return getUserAccessObject().setUserPayloadAccess(chNum, operation, userId,
+                                                      payloadAccess);
+}
+
+ipmi_ret_t ipmiUserGetUserPayloadAccess(const uint8_t chNum,
+                                        const uint8_t userId,
+                                        PayloadAccess& payloadAccess)
+{
+
+    if (!UserAccess::isValidChannel(chNum))
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    if (!UserAccess::isValidUserId(userId))
+    {
+        return IPMI_CC_PARM_OUT_OF_RANGE;
+    }
+
+    UserInfo* userInfo = getUserAccessObject().getUserInfo(userId);
+
+    payloadAccess.stdPayloadEnables1 =
+        userInfo->payloadAccess[chNum].stdPayloadEnables1;
+    payloadAccess.stdPayloadEnables2Reserved =
+        userInfo->payloadAccess[chNum].stdPayloadEnables2Reserved;
+    payloadAccess.oemPayloadEnables1 =
+        userInfo->payloadAccess[chNum].oemPayloadEnables1;
+    payloadAccess.oemPayloadEnables2Reserved =
+        userInfo->payloadAccess[chNum].oemPayloadEnables2Reserved;
+
+    return IPMI_CC_OK;
+}
+
 } // namespace ipmi
diff --git a/user_channel/user_layer.hpp b/user_channel/user_layer.hpp
index 7926c59..450d878 100644
--- a/user_channel/user_layer.hpp
+++ b/user_channel/user_layer.hpp
@@ -16,6 +16,7 @@
 #pragma once
 #include <ipmid/api.h>
 
+#include <bitset>
 #include <string>
 
 namespace ipmi
@@ -37,6 +38,7 @@
 static constexpr uint8_t ipmiMaxChannels = 16;
 static constexpr uint8_t maxIpmi20PasswordSize = 20;
 static constexpr uint8_t maxIpmi15PasswordSize = 16;
+static constexpr uint8_t payloadsPerByte = 8;
 
 /** @struct PrivAccess
  *
@@ -61,6 +63,19 @@
 #endif
 } __attribute__((packed));
 
+/** @struct UserPayloadAccess
+ *
+ *  Structure to denote payload access restrictions applicable for a
+ *  given user and channel. (refer spec sec 24.6)
+ */
+struct PayloadAccess
+{
+    std::bitset<payloadsPerByte> stdPayloadEnables1;
+    std::bitset<payloadsPerByte> stdPayloadEnables2Reserved;
+    std::bitset<payloadsPerByte> oemPayloadEnables1;
+    std::bitset<payloadsPerByte> oemPayloadEnables2Reserved;
+};
+
 /** @brief initializes user management
  *
  *  @return IPMI_CC_OK for success, others for failure.
@@ -221,4 +236,30 @@
 bool ipmiUserPamAuthenticate(std::string_view userName,
                              std::string_view userPassword);
 
+/** @brief sets user payload access data
+ *
+ *  @param[in] chNum - channel number
+ *  @param[in] operation - ENABLE / DISABLE operation
+ *  @param[in] userId - user id
+ *  @param[in] payloadAccess - payload access data
+ *
+ *  @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserSetUserPayloadAccess(const uint8_t chNum,
+                                        const uint8_t operation,
+                                        const uint8_t userId,
+                                        const PayloadAccess& payloadAccess);
+
+/** @brief provides user payload access data
+ *
+ *  @param[in] chNum - channel number
+ *  @param[in] userId - user id
+ *  @param[out] payloadAccess - payload access data
+ *
+ *  @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserGetUserPayloadAccess(const uint8_t chNum,
+                                        const uint8_t userId,
+                                        PayloadAccess& payloadAccess);
+
 } // namespace ipmi
diff --git a/user_channel/user_mgmt.cpp b/user_channel/user_mgmt.cpp
index 9b40f6c..f877981 100644
--- a/user_channel/user_mgmt.cpp
+++ b/user_channel/user_mgmt.cpp
@@ -16,6 +16,7 @@
 #include "user_mgmt.hpp"
 
 #include "apphandler.hpp"
+#include "channel_layer.hpp"
 
 #include <security/pam_appl.h>
 #include <sys/stat.h>
@@ -833,6 +834,60 @@
     return IPMI_CC_OK;
 }
 
+ipmi_ret_t UserAccess::setUserPayloadAccess(const uint8_t chNum,
+                                            const uint8_t operation,
+                                            const uint8_t userId,
+                                            const PayloadAccess& payloadAccess)
+{
+    constexpr uint8_t enable = 0x0;
+    constexpr uint8_t disable = 0x1;
+
+    if (!isValidChannel(chNum))
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    if (!isValidUserId(userId))
+    {
+        return IPMI_CC_PARM_OUT_OF_RANGE;
+    }
+    if (operation != enable && operation != disable)
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    // Check operation & payloadAccess if required.
+    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+        userLock{*userMutex};
+    UserInfo* userInfo = getUserInfo(userId);
+
+    if (operation == enable)
+    {
+        userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
+            payloadAccess.stdPayloadEnables1;
+
+        userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
+            payloadAccess.oemPayloadEnables1;
+    }
+    else
+    {
+        userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
+            ~(payloadAccess.stdPayloadEnables1);
+
+        userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
+            ~(payloadAccess.oemPayloadEnables1);
+    }
+
+    try
+    {
+        writeUserData();
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("Write user data failed");
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+    return IPMI_CC_OK;
+}
+
 ipmi_ret_t UserAccess::setUserPrivilegeAccess(const uint8_t userId,
                                               const uint8_t chNum,
                                               const UserPrivAccess& privAccess,
@@ -1040,6 +1095,98 @@
 static constexpr const char* jsonUserEnabled = "user_enabled";
 static constexpr const char* jsonUserInSys = "user_in_system";
 static constexpr const char* jsonFixedUser = "fixed_user_name";
+static constexpr const char* payloadEnabledStr = "payload_enabled";
+static constexpr const char* stdPayloadStr = "std_payload";
+static constexpr const char* oemPayloadStr = "OEM_payload";
+
+/** @brief to construct a JSON object from the given payload access details.
+ *
+ *  @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
+ *  @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
+ *
+ *  @details Sample output JSON object format :
+ *  "payload_enabled":{
+ *  "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ *  }
+ */
+static const Json constructJsonPayloadEnables(
+    const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+        stdPayload,
+    const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+        oemPayload)
+{
+    Json jsonPayloadEnabled;
+
+    for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
+    {
+        std::ostringstream stdPayloadStream;
+        std::ostringstream oemPayloadStream;
+
+        stdPayloadStream << stdPayloadStr << payloadNum;
+        oemPayloadStream << oemPayloadStr << payloadNum;
+
+        jsonPayloadEnabled.push_back(Json::object_t::value_type(
+            stdPayloadStream.str(), stdPayload[payloadNum]));
+
+        jsonPayloadEnabled.push_back(Json::object_t::value_type(
+            oemPayloadStream.str(), oemPayload[payloadNum]));
+    }
+    return jsonPayloadEnabled;
+}
+
+void UserAccess::readPayloadAccessFromUserInfo(
+    const UserInfo& userInfo,
+    std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
+    std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
+{
+    for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
+    {
+        for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
+        {
+            stdPayload[payloadNum][chIndex] =
+                userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
+
+            oemPayload[payloadNum][chIndex] =
+                userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
+        }
+    }
+}
+
+void UserAccess::updatePayloadAccessInUserInfo(
+    const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+        stdPayload,
+    const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+        oemPayload,
+    UserInfo& userInfo)
+{
+    for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
+    {
+        // Ensure that reserved/unsupported payloads are marked to zero.
+        userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
+        userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
+        userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
+        userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
+        // Update SOL status as it is the only supported payload currently.
+        userInfo.payloadAccess[chIndex]
+            .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
+            stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
+    }
+}
 
 void UserAccess::readUserData()
 {
@@ -1063,6 +1210,15 @@
         throw std::runtime_error(
             "Corrupted IPMI user data file - invalid user count");
     }
+
+    // Construct a JSON object with default payload access values.
+    std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte> stdPayload =
+        {};
+    std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte> oemPayload =
+        {};
+    static const Json jsonPayloadEnabledDefault =
+        constructJsonPayloadEnables(stdPayload, oemPayload);
+
     // user index 0 is reserved, starts with 1
     for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
     {
@@ -1086,6 +1242,36 @@
             userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
         std::vector<bool> accessCallback =
             userInfo[jsonAccCallbk].get<std::vector<bool>>();
+
+        // Payload Enables Processing.
+        auto jsonPayloadEnabled =
+            userInfo.value<Json>(payloadEnabledStr, jsonPayloadEnabledDefault);
+
+        for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
+        {
+            std::ostringstream stdPayloadStream;
+            std::ostringstream oemPayloadStream;
+
+            stdPayloadStream << stdPayloadStr << payloadNum;
+            oemPayloadStream << oemPayloadStr << payloadNum;
+
+            stdPayload[payloadNum] =
+                jsonPayloadEnabled[stdPayloadStream.str()]
+                    .get<std::array<bool, ipmiMaxChannels>>();
+            oemPayload[payloadNum] =
+                jsonPayloadEnabled[oemPayloadStream.str()]
+                    .get<std::array<bool, ipmiMaxChannels>>();
+
+            if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
+                oemPayload[payloadNum].size() != ipmiMaxChannels)
+            {
+                log<level::ERR>("Error in reading IPMI user data file - "
+                                "payload properties corrupted");
+                throw std::runtime_error(
+                    "Corrupted IPMI user data file - payload properties");
+            }
+        }
+
         if (privilege.size() != ipmiMaxChannels ||
             ipmiEnabled.size() != ipmiMaxChannels ||
             linkAuthEnabled.size() != ipmiMaxChannels ||
@@ -1108,6 +1294,8 @@
             usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
                 accessCallback[chIndex];
         }
+        updatePayloadAccessInUserInfo(stdPayload, oemPayload,
+                                      usersTbl.user[usrIndex]);
         usersTbl.user[usrIndex].userEnabled =
             userInfo[jsonUserEnabled].get<bool>();
         usersTbl.user[usrIndex].userInSystem =
@@ -1140,6 +1328,12 @@
         std::vector<bool> ipmiEnabled(ipmiMaxChannels);
         std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
         std::vector<bool> accessCallback(ipmiMaxChannels);
+
+        std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
+            stdPayload;
+        std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
+            oemPayload;
+
         for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
         {
             privilege[chIndex] =
@@ -1159,6 +1353,13 @@
         jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
         jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
         jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
+
+        readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
+                                      oemPayload);
+        Json jsonPayloadEnabledInfo =
+            constructJsonPayloadEnables(stdPayload, oemPayload);
+        jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
+
         jsonUsersTbl.push_back(jsonUserInfo);
     }
 
diff --git a/user_channel/user_mgmt.hpp b/user_channel/user_mgmt.hpp
index 8b650c8..773b18d 100644
--- a/user_channel/user_mgmt.hpp
+++ b/user_channel/user_mgmt.hpp
@@ -75,6 +75,7 @@
     bool userEnabled;
     bool userInSystem;
     bool fixedUserName;
+    PayloadAccess payloadAccess[ipmiMaxChannels];
 };
 
 /** @struct UsersTbl
@@ -252,6 +253,56 @@
                                       const UserPrivAccess& privAccess,
                                       const bool& otherPrivUpdates);
 
+    /** @brief to get user payload access details from userInfo entry.
+     *
+     *  @param[in] userInfo    - userInfo entry in usersTbl.
+     *  @param[out] stdPayload - stdPayloadEnables1 in a 2D-array.
+     *  @param[out] oemPayload - oemPayloadEnables1 in a 2D-array.
+     *
+     *  @details Update the given 2D-arrays using the payload access details
+     *  available in the given userInfo entry (from usersTbl).
+     *  This 2D-array will be mapped to a JSON object (which will be written to
+     *  a JSON file subsequently).
+     */
+    void readPayloadAccessFromUserInfo(
+        const UserInfo& userInfo,
+        std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+            stdPayload,
+        std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+            oemPayload);
+
+    /** @brief to update user payload access details in userInfo entry.
+     *
+     *  @param[in] stdPayload - stdPayloadEnables1 in a 2D-array.
+     *  @param[in] oemPayload - oemPayloadEnables1 in a 2D-array.
+     *  @param[out] userInfo  - userInfo entry in usersTbl.
+     *
+     *  @details Update user payload access details of a given userInfo
+     *  entry (in usersTbl) with the information provided in given 2D-arrays.
+     *  This 2D-array was created out of a JSON object (which was created by
+     *  parsing a JSON file).
+     */
+    void updatePayloadAccessInUserInfo(
+        const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+            stdPayload,
+        const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+            oemPayload,
+        UserInfo& userInfo);
+
+    /** @brief to set user payload access details
+     *
+     *  @param[in] chNum - channel number
+     *  @param[in] operation - Enable / Disable
+     *  @param[in] userId - user id
+     *  @param[in] payloadAccess - payload access
+     *
+     *  @return IPMI_CC_OK for success, others for failure.
+     */
+    ipmi_ret_t setUserPayloadAccess(const uint8_t chNum,
+                                    const uint8_t operation,
+                                    const uint8_t userId,
+                                    const PayloadAccess& payloadAccess);
+
     /** @brief reads user management related data from configuration file
      *
      */
diff --git a/user_channel/usercommands.cpp b/user_channel/usercommands.cpp
index 5bdbbd3..50fb52b 100644
--- a/user_channel/usercommands.cpp
+++ b/user_channel/usercommands.cpp
@@ -37,6 +37,8 @@
 static constexpr uint8_t testPassword = 0x03;
 static constexpr uint8_t passwordKeySize20 = 1;
 static constexpr uint8_t passwordKeySize16 = 0;
+static constexpr uint8_t enableOperation = 0x00;
+static constexpr uint8_t disableOperation = 0x01;
 
 /** @struct SetUserNameReq
  *
@@ -485,6 +487,188 @@
         rmcp, rmcpp, reserved5, oemID, oemAuxillary);
 }
 
+/** @brief implements the set user payload access command.
+ *  @param ctx - IPMI context pointer (for channel)
+ *  @param channel - channel number (4 bits)
+ *  @param reserved1 - skip 4 bits
+ *  @param userId - user id (6 bits)
+ *  @param operation - access ENABLE /DISABLE. (2 bits)
+ *  @param stdPayload0 - IPMI - reserved. (1 bit)
+ *  @param stdPayload1 - SOL.             (1 bit)
+ *  @param stdPayload2 -                  (1 bit)
+ *  @param stdPayload3 -                  (1 bit)
+ *  @param stdPayload4 -                  (1 bit)
+ *  @param stdPayload5 -                  (1 bit)
+ *  @param stdPayload6 -                  (1 bit)
+ *  @param stdPayload7 -                  (1 bit)
+ *  @param stdPayloadEnables2Reserved -   (8 bits)
+ *  @param oemPayload0 -                  (1 bit)
+ *  @param oemPayload1 -                  (1 bit)
+ *  @param oemPayload2 -                  (1 bit)
+ *  @param oemPayload3 -                  (1 bit)
+ *  @param oemPayload4 -                  (1 bit)
+ *  @param oemPayload5 -                  (1 bit)
+ *  @param oemPayload6 -                  (1 bit)
+ *  @param oemPayload7 -                  (1 bit)
+ *  @param oemPayloadEnables2Reserved -   (8 bits)
+ *
+ *  @returns IPMI completion code
+ */
+ipmi::RspType<> ipmiSetUserPayloadAccess(
+    ipmi::Context::ptr ctx,
+
+    uint4_t channel, uint4_t reserved,
+
+    uint6_t userId, uint2_t operation,
+
+    bool stdPayload0ipmiReserved, bool stdPayload1SOL, bool stdPayload2,
+    bool stdPayload3, bool stdPayload4, bool stdPayload5, bool stdPayload6,
+    bool stdPayload7,
+
+    uint8_t stdPayloadEnables2Reserved,
+
+    bool oemPayload0, bool oemPayload1, bool oemPayload2, bool oemPayload3,
+    bool oemPayload4, bool oemPayload5, bool oemPayload6, bool oemPayload7,
+
+    uint8_t oemPayloadEnables2Reserved)
+{
+    // Validate the reserved args. Only SOL payload is supported as on date.
+    if (reserved || stdPayload0ipmiReserved || stdPayload2 || stdPayload3 ||
+        stdPayload4 || stdPayload5 || stdPayload6 || stdPayload7 ||
+        oemPayload0 || oemPayload1 || oemPayload2 || oemPayload3 ||
+        oemPayload4 || oemPayload5 || oemPayload6 || oemPayload7 ||
+        stdPayloadEnables2Reserved || oemPayloadEnables2Reserved)
+    {
+        return ipmi::responseInvalidFieldRequest();
+    }
+
+    auto chNum =
+        convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
+    if ((operation != enableOperation && operation != disableOperation) ||
+        (!isValidChannel(chNum)) ||
+        (getChannelSessionSupport(chNum) == EChannelSessSupported::none))
+    {
+        return ipmi::responseInvalidFieldRequest();
+    }
+
+    if (!ipmiUserIsValidUserId(static_cast<uint8_t>(userId)))
+    {
+        return ipmi::responseParmOutOfRange();
+    }
+
+    PayloadAccess payloadAccess = {0};
+    payloadAccess.stdPayloadEnables1[1] = stdPayload1SOL;
+
+    return ipmi::response(ipmiUserSetUserPayloadAccess(
+        chNum, static_cast<uint8_t>(operation), static_cast<uint8_t>(userId),
+        payloadAccess));
+}
+
+/** @brief implements the get user payload access command
+ *  This command returns information about user payload enable settings
+ *  that were set using the 'Set User Payload Access' Command.
+ *
+ *  @param ctx - IPMI context pointer (for channel)
+ *  @param channel - channel number
+ *  @param reserved1 - skip 4 bits
+ *  @param userId - user id
+ *  @param reserved2 - skip 2 bits
+ *
+ *  @returns IPMI completion code plus response data
+ *   - stdPayload0ipmiReserved - IPMI payload (reserved).
+ *   - stdPayload1SOL - SOL payload
+ *   - stdPayload2
+ *   - stdPayload3
+ *   - stdPayload4
+ *   - stdPayload5
+ *   - stdPayload6
+ *   - stdPayload7
+
+ *   - stdPayloadEnables2Reserved - Reserved.
+
+ *   - oemPayload0
+ *   - oemPayload1
+ *   - oemPayload2
+ *   - oemPayload3
+ *   - oemPayload4
+ *   - oemPayload5
+ *   - oemPayload6
+ *   - oemPayload7
+
+ *  - oemPayloadEnables2Reserved - Reserved
+ */
+ipmi::RspType<bool, // stdPayload0ipmiReserved
+              bool, // stdPayload1SOL
+              bool, // stdPayload2
+              bool, // stdPayload3
+              bool, // stdPayload4
+              bool, // stdPayload5
+              bool, // stdPayload6
+              bool, // stdPayload7
+
+              uint8_t, // stdPayloadEnables2Reserved
+
+              bool, // oemPayload0
+              bool, // oemPayload1
+              bool, // oemPayload2
+              bool, // oemPayload3
+              bool, // oemPayload4
+              bool, // oemPayload5
+              bool, // oemPayload6
+              bool, // oemPayload7
+
+              uint8_t // oemPayloadEnables2Reserved
+              >
+    ipmiGetUserPayloadAccess(ipmi::Context::ptr ctx,
+
+                             uint4_t channel, uint4_t reserved1,
+
+                             uint6_t userId, uint2_t reserved2)
+{
+    uint8_t chNum =
+        convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
+    if (reserved1 != 0 || reserved2 != 0 || (!isValidChannel(chNum)) ||
+        (getChannelSessionSupport(chNum) == EChannelSessSupported::none))
+    {
+        return ipmi::responseInvalidFieldRequest();
+    }
+    if (!ipmiUserIsValidUserId(static_cast<uint8_t>(userId)))
+    {
+        return ipmi::responseParmOutOfRange();
+    }
+
+    ipmi::Cc retStatus;
+    PayloadAccess payloadAccess = {};
+    retStatus = ipmiUserGetUserPayloadAccess(
+        chNum, static_cast<uint8_t>(userId), payloadAccess);
+    if (retStatus != IPMI_CC_OK)
+    {
+        return ipmi::response(retStatus);
+    }
+    constexpr uint8_t res8bits = 0;
+    return ipmi::responseSuccess(payloadAccess.stdPayloadEnables1.test(0),
+                                 payloadAccess.stdPayloadEnables1.test(1),
+                                 payloadAccess.stdPayloadEnables1.test(2),
+                                 payloadAccess.stdPayloadEnables1.test(3),
+                                 payloadAccess.stdPayloadEnables1.test(4),
+                                 payloadAccess.stdPayloadEnables1.test(5),
+                                 payloadAccess.stdPayloadEnables1.test(6),
+                                 payloadAccess.stdPayloadEnables1.test(7),
+
+                                 res8bits,
+
+                                 payloadAccess.oemPayloadEnables1.test(0),
+                                 payloadAccess.oemPayloadEnables1.test(1),
+                                 payloadAccess.oemPayloadEnables1.test(2),
+                                 payloadAccess.oemPayloadEnables1.test(3),
+                                 payloadAccess.oemPayloadEnables1.test(4),
+                                 payloadAccess.oemPayloadEnables1.test(5),
+                                 payloadAccess.oemPayloadEnables1.test(6),
+                                 payloadAccess.oemPayloadEnables1.test(7),
+
+                                 res8bits);
+}
+
 void registerUserIpmiFunctions() __attribute__((constructor));
 void registerUserIpmiFunctions()
 {
@@ -510,6 +694,15 @@
                           ipmi::app::cmdGetChannelAuthCapabilities,
                           ipmi::Privilege::Callback,
                           ipmiGetChannelAuthenticationCapabilities);
+
+    ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+                          ipmi::app::cmdSetUserPayloadAccess,
+                          ipmi::Privilege::Admin, ipmiSetUserPayloadAccess);
+
+    ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+                          ipmi::app::cmdGetUserPayloadAccess,
+                          ipmi::Privilege::Operator, ipmiGetUserPayloadAccess);
+
     return;
 }
 } // namespace ipmi