Intel OEM commands

Intel platform defines a set of OEM commands. They are required by
Intel BIOS.

This change adds 5 OEM commands:
1.get chassis identifier
Return chassis serial number

2.set system GUID
Sets the system GUID obtained according to the format of the IPMI
2.0 Get System GUID command. While the setting is persistent in the
BMC, the BIOS may choose to over-write the value during POST.

3.set BIOS ID
BIOS version string

4.get device info
BMC/HSC/ME version

5.get AIC(add-in-card) FRU
Add-in-card FRU info

Tested: Test with ipmitool raw command on Intel S2600WF
Platform. BIOS calling is also verified by checking ipmi handler
log (journalctl -ef).

Change-Id: I658c5845cd1252f55dc5d24dfc142281b6a38363
Signed-off-by: Jia, Chunhui <chunhui.jia@linux.intel.com>
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..dd27708
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,98 @@
+---
+Language:        Cpp
+# BasedOnStyle:  LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+  AfterClass:      true
+  AfterControlStatement: true
+  AfterEnum:       true
+  AfterFunction:   true
+  AfterNamespace:  true
+  AfterObjCDeclaration: true
+  AfterStruct:     true
+  AfterUnion:      true
+  BeforeCatch:     true
+  BeforeElse:      true
+  IndentBraces:    false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit:     80
+CommentPragmas:  '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+PointerAlignment: Left
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeBlocks: Regroup
+IncludeCategories:
+  - Regex:           '^[<"](gtest|gmock)'
+    Priority:        5
+  - Regex:           '^"config.h"'
+    Priority:        -1
+  - Regex:           '^".*\.hpp"'
+    Priority:        1
+  - Regex:           '^<.*\.h>'
+    Priority:        2
+  - Regex:           '^<.*'
+    Priority:        3
+  - Regex:           '.*'
+    Priority:        4
+IndentCaseLabels: true
+IndentWidth:     4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments:  true
+SortIncludes:    true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Cpp11
+TabWidth:        4
+UseTab:          Never
+...
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e0c197e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/build*
+.vscode
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..f8344f8
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,59 @@
+cmake_minimum_required (VERSION 3.5 FATAL_ERROR)
+
+cmake_policy (SET CMP0054 NEW)
+
+option (YOCTO_DEPENDENCIES "Use YOCTO depedencies system" OFF)
+include (ExternalProject)
+set (CMAKE_CXX_STANDARD 17)
+set (CMAKE_CXX_STANDARD_REQUIRED ON)
+set (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
+
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
+
+project (intel-ipmi-oem CXX)
+
+add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY)
+add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED)
+add_definitions (-DBOOST_ALL_NO_LIB)
+add_definitions (-DBOOST_NO_RTTI)
+add_definitions (-DBOOST_NO_TYPEID)
+
+if (NOT ${YOCTO_DEPENDENCIES})
+    configure_file (CMakeLists.txt.in 3rdparty/CMakeLists.txt)
+    execute_process (
+        COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3rdparty
+    )
+    execute_process (
+        COMMAND ${CMAKE_COMMAND} --build .
+        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3rdparty
+    )
+
+    set (CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}/prefix ${CMAKE_PREFIX_PATH})
+    include_directories (${CMAKE_BINARY_DIR}/prefix/include)
+endif ()
+
+# sdbusplus
+if (NOT ${YOCTO_DEPENDENCIES})
+    include_directories (${CMAKE_BINARY_DIR}/sdbusplus-src)
+    link_directories (${CMAKE_BINARY_DIR}/sdbusplus-src/.libs)
+endif ()
+
+
+if (${YOCTO_DEPENDENCIES})
+    find_package (PkgConfig REQUIRED)
+    pkg_check_modules (LOGGING phosphor-logging REQUIRED)
+    include_directories (${LOGGING_INCLUDE_DIRS})
+    link_directories (${LOGGING_LIBRARY_DIRS})
+endif ()
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+add_library (oemcmds SHARED src/oemcommands.cpp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/utils.cpp)
+set_target_properties (oemcmds PROPERTIES VERSION "0.1.0")
+set_target_properties (oemcmds PROPERTIES SOVERSION "0")
+target_link_libraries (oemcmds sdbusplus)
+target_link_libraries (oemcmds ${LOGGING_LIBRARIES})
+
+install (TARGETS oemcmds DESTINATION lib/ipmid-providers)
+
diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in
new file mode 100644
index 0000000..a2d0136
--- /dev/null
+++ b/CMakeLists.txt.in
@@ -0,0 +1,126 @@
+cmake_minimum_required (VERSION 3.5)
+
+include (ExternalProject)
+
+file (MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/prefix)
+file (MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/prefix/include)
+
+# requires apt install autoconf-archive and autoconf
+externalproject_add (
+    sdbusplus-project PREFIX ${CMAKE_BINARY_DIR}/sdbusplus-project
+    GIT_REPOSITORY https://github.com/openbmc/sdbusplus.git
+    GIT_TAG    69425eb7d30816c03f88962c44b12b6de5b3cc71
+    SOURCE_DIR    ${CMAKE_BINARY_DIR}/sdbusplus-src
+    BINARY_DIR    ${CMAKE_BINARY_DIR}/sdbusplus-build CONFIGURE_COMMAND ""
+    BUILD_COMMAND
+     cd ${CMAKE_BINARY_DIR}/sdbusplus-src &&
+     ./bootstrap.sh clean &&
+     ./bootstrap.sh &&
+     ./configure --prefix=${CMAKE_BINARY_DIR}/prefix CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/
+                 --enable-transaction &&
+     make && make install
+    INSTALL_COMMAND ""
+    LOG_DOWNLOAD ON
+)
+
+externalproject_add (
+    dbus-interfaces
+    PREFIX ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces
+    DEPENDS sdbusplus-project
+    GIT_REPOSITORY https://github.com/openbmc/phosphor-dbus-interfaces
+    GIT_TAG 4132f4b6b1de57a993af9bd2bcd039957786a227
+    SOURCE_DIR    ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-src
+    BINARY_DIR    ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-build
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND
+     cd ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-src &&
+     echo "#!/bin/bash" > build.sh &&
+     echo "export PYTHONPATH=${CMAKE_BINARY_DIR}/prefix/lib/python2.7/site-packages:$ENV{PYTHONPATH}" >> build.sh  &&
+     echo "export PATH=${CMAKE_BINARY_DIR}/prefix/bin:$ENV{PATH}" >> build.sh  &&
+     echo "export PKG_CONFIG_PATH=${CMAKE_BINARY_DIR}/prefix/lib/pkgconfig" >> build.sh  &&
+     echo "./bootstrap.sh " >> build.sh  &&
+     echo "./configure --prefix=${CMAKE_BINARY_DIR}/prefix  CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/ " >> build.sh  &&
+     echo "make verbose=1" >> build.sh  &&
+     echo "make install " >> build.sh  &&
+     chmod 777 build.sh  &&
+     ./build.sh
+    INSTALL_COMMAND ""
+    LOG_DOWNLOAD ON
+)
+
+externalproject_add (
+    cereal
+    GIT_REPOSITORY https://github.com/USCiLab/cereal
+    GIT_TAG     51cbda5f30e56c801c07fe3d3aba5d7fb9e6cca4
+    SOURCE_DIR  "${CMAKE_BINARY_DIR}/cereal-src"
+    BINARY_DIR  "${CMAKE_BINARY_DIR}/cereal-build"
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND  ""
+    INSTALL_COMMAND
+       mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/cereal" &&
+       cp -r "${CMAKE_BINARY_DIR}/cereal-src/include/cereal"
+       "${CMAKE_BINARY_DIR}/prefix/include"
+)
+
+
+externalproject_add (
+    phosphor-logging
+    PREFIX ${CMAKE_BINARY_DIR}/phosphor-logging
+    DEPENDS cereal sdbusplus-project dbus-interfaces
+    GIT_REPOSITORY https://github.com/openbmc/phosphor-logging
+    GIT_TAG    477b731ad0fd8c116ffcaa8265a508c9fb112479
+    SOURCE_DIR ${CMAKE_BINARY_DIR}/phosphor-logging-src
+    BINARY_DIR ${CMAKE_BINARY_DIR}/phosphor-logging-build
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND
+     cd ${CMAKE_BINARY_DIR}/phosphor-logging-src &&
+     echo "#!/bin/bash" > build.sh &&
+     echo "export PYTHONPATH=${CMAKE_BINARY_DIR}/prefix/lib/python2.7/site-packages:$ENV{PYTHONPATH}" >> build.sh  &&
+     echo "export PATH=${CMAKE_BINARY_DIR}/prefix/bin:$ENV{PATH}" >> build.sh  &&
+     echo "export PKG_CONFIG_PATH=${CMAKE_BINARY_DIR}/prefix/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}" >> build.sh  &&
+     echo "./bootstrap.sh clean" >> build.sh  &&
+     echo "./bootstrap.sh" >> build.sh  &&
+     echo "./configure --prefix=${CMAKE_BINARY_DIR}/prefix  CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/ \
+             YAML_DIR=${CMAKE_BINARY_DIR}/prefix/share/phosphor-dbus-yaml/yaml --enable-metadata-processing" >> build.sh  &&
+     echo "make verbose=1" >> build.sh  &&
+     echo "make install " >> build.sh  &&
+     chmod 777 build.sh  &&
+     ./build.sh
+    INSTALL_COMMAND  ""
+    LOG_DOWNLOAD ON
+)
+
+externalproject_add (
+    nlohmann-json
+    GIT_REPOSITORY "https://github.com/nlohmann/json.git"
+    GIT_TAG d2dd27dc3b8472dbaa7d66f83619b3ebcd9185fe
+    SOURCE_DIR "${CMAKE_BINARY_DIR}/nlohmann-json-src"
+    BINARY_DIR "${CMAKE_BINARY_DIR}/nlohmann-json-build"
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND  ""
+    INSTALL_COMMAND
+       mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/nlohmann" &&
+       cp -r "${CMAKE_BINARY_DIR}/nlohmann-json-src/include/nlohmann"
+       "${CMAKE_BINARY_DIR}/prefix/include"
+)
+
+externalproject_add (
+    host-ipmid
+    PREFIX ${CMAKE_BINARY_DIR}/phosphor-host-ipmid
+    DEPENDS sdbusplus-project dbus-interfaces nlohmann-json
+    GIT_REPOSITORY https://github.com/openbmc/phosphor-host-ipmid
+    SOURCE_DIR ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src
+    BINARY_DIR ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-build
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND ""
+    INSTALL_COMMAND
+     mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/host-ipmid" &&
+     cp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/host-ipmid/ipmid-api.h ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid &&
+     cp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/elog-errors.hpp ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid &&
+     cp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/utils.hpp ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid &&
+     cp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/types.hpp ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid
+    LOG_DOWNLOAD ON
+)
+
+
+
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 0000000..28a9120
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,68 @@
+List of maintainers for intel-ipmi-oem
+===============================
+
+How to use this list:
+    Find the most specific section entry (described below) that matches where
+    your change lives and add the reviewers (R) and maintainers (M) as
+    reviewers. You can use the same method to track down who knows a particular
+    code base best.
+
+    Your change/query may span multiple entries; that is okay.
+
+    If you do not find an entry that describes your request at all, someone
+    forgot to update this list; please at least file an issue or send an email
+    to a maintainer, but preferably you should just update this document.
+
+Description of section entries:
+
+    Section entries are structured according to the following scheme:
+
+    X:  NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>
+    X:  ...
+    .
+    .
+    .
+
+    Where REPO_NAME is the name of the repository within the OpenBMC GitHub
+    organization; FILE_PATH is a file path within the repository, possibly with
+    wildcards; X is a tag of one of the following types:
+
+    M:  Denotes maintainer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+        if omitted from an entry, assume one of the maintainers from the
+        MAINTAINERS entry.
+    R:  Denotes reviewer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+        these people are to be added as reviewers for a change matching the repo
+        path.
+    F:  Denotes forked from an external repository; has fields URL.
+
+    Line comments are to be denoted "# SOME COMMENT" (typical shell style
+    comment); it is important to follow the correct syntax and semantics as we
+    may want to use automated tools with this file in the future.
+
+    A change cannot be added to an OpenBMC repository without a MAINTAINER's
+    approval; thus, a MAINTAINER should always be listed as a reviewer.
+
+Change approval rules:
+
+    - Patches must be available for review for a minimum of 48 hours before it
+      can be submitted.
+    - Patches must be be approved (+1) by at least 2 maintainers.
+    - Patches must not have an unresolved -1 vote by any maintainer.
+    - Patches should have all maintainers added for visibility.
+    - Patches should include unit tests where possible.
+    - Feel free to ping on IRC about patches that look good but have not
+      received +2
+
+Design approval rules:
+
+    - Design discussions should be carried out via email with, at minimum,
+      all maintainers on the thread.  It's encouraged to include the
+      OpenBMC mailing list in the thread as well.
+
+START OF MAINTAINERS LIST
+-------------------------
+
+M:  Ed Tanous <ed.tanous@intel.com> <edtanous!>
+M:  Vernon Mauery <vernon.mauery@linux.intel.com> <vmauery!>
+R:  Chunhui Jia <chunhui.jia@@linux.intel.com> <cjia_!>
+R:  James Feist <james.feist@intel.com> <jfei!>
diff --git a/include/oemcommands.hpp b/include/oemcommands.hpp
new file mode 100644
index 0000000..ed4b57a
--- /dev/null
+++ b/include/oemcommands.hpp
@@ -0,0 +1,161 @@
+/*
+// Copyright (c) 2018 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.
+*/
+
+#pragma once
+
+enum IPMINetfnIntelOEMGeneralCmd
+{
+    cmdSetBIOSID = 0x26,
+    cmdGetOEMDeviceInfo = 0x27,
+    cmdGetAICSlotFRUIDRecords = 0x31,
+    cmdSetSystemGUID = 0x41,
+    cmdGetChassisIdentifier = 0x92,
+};
+
+enum IPMIIntelOEMReturnCodes
+{
+    ipmiCCPayloadActive = 0x80,
+    ipmiCCInvalidPCIESlotID = 0x80,
+    ipmiCCParameterNotSupported = 0x80,
+    ipmiCCPayloadAlreadyDeactivated = 0x80,
+    ipmiCCSetInProcess = 0x81,
+    ipmiCCPayloadDisable = 0x81,
+    ipmiCCLostArbitration = 0x81,
+    ipmiCCInvalidCablePortIndex = 0x81,
+    ipmiCCHealthStatusNotAvailable = 0x81,
+    ipmiCCBusError = 0x82,
+    ipmiCCReadOnly = 0x82,
+    ipmiCCWriteOnly = 0x82,
+    ipmiCCNoCablePresent = 0x82,
+    ipmiCCDataCollectionInProgress = 0x82,
+    ipmiCCPayloadActivationLimitReached = 0x82,
+    ipmiCCNACKOnWrite = 0x83,
+    ipmiCCDataCollectionFailed = 0x83,
+    ipmiCCCanNotActivateWithEncrption = 0x83,
+    ipmiCCCanNotActivateWithoutEncryption = 0x84,
+    ipmiCCInvalidChecksum = 0x85,
+    ipmiCCNoCabledPCIEPortsAvailable = 0xC2,
+
+};
+
+enum IPMIReturnCodeExt
+{
+    ipmiCCInvalidLUN = 0xC2,
+    ipmiCCTimeout = 0xC3,
+    ipmiCCStorageLeak = 0xC4,
+    ipmiCCRequestDataTruncated = 0xC6,
+    ipmiCCRequestDataFieldLengthLimitExceeded = 0xC8,
+    ipmiCCCanNotReturnNumberOfRequestedDataBytes = 0xCA,
+    ipmiCCRequestSensorDataRecordNotFound = 0xCB,
+    ipmiCCDestinationUnavailable = 0xD3,
+    ipmiCCParamterNotSupportInPresentState = 0xD5,
+};
+
+constexpr unsigned char netfunIntelAppOEM = 0x3E;
+static constexpr ipmi_netfn_t netfunIntcOEMGeneral =
+    NETFUN_NONE; // Netfun_none. In our platform, we use it as "intel oem
+                 // general". The code is 0x30
+static constexpr unsigned char maxBiosIdLength = 0xFF;
+static constexpr char* biosObjPath = (char*)"/xyz/openbmc_project/bios";
+static constexpr char* biosIntf =
+    (char*)"xyz.openbmc_project.Inventory.Item.Bios";
+static constexpr char* biosProp = (char*)"BiosId";
+
+enum IPMINetfnIntelOEMAppCmd
+{
+    mdrStatus = 0x20,
+    mdrComplete = 0x21,
+    mdrEvent = 0x22,
+    mdrRead = 0x23,
+    mdrWrite = 0x24,
+    mdrLock = 0x25,
+    mdr2AgentStatus = 0x30,
+    mdr2GetDir = 0x31,
+    mdr2GetDataInfo = 0x32,
+    mdr2LockData = 0x33,
+    mdr2UnlockData = 0x34,
+    mdr2GetDataBlock = 0x35,
+    mdr2SendDir = 0x38,
+    mdr2SendDataInfoOffer = 0x39,
+    mdr2SendDataInfo = 0x3a,
+    mdr2DataStart = 0x3b,
+    mdr2DataDone = 0x3c,
+    mdr2SendDataBlock = 0x3d,
+};
+
+typedef enum
+{
+    biosId,
+    devVer,
+    sdrVer,
+} OEMDevEntityType;
+
+typedef union
+{
+    typedef struct
+    {
+        uint8_t bBrdSlotNum : 3;  // Bits 2:0
+        uint8_t riserSlotNum : 3; // Bits 5:3
+        uint8_t protocol : 1;     // Bit 6, FRU type
+        uint8_t reserved : 1;     // Bit 7
+    } bits;
+    uint8_t byte;
+} __attribute__((packed)) AICFruRec;
+
+typedef struct
+{
+    AICFruRec u8SlotPosition;
+    uint8_t u8FruID;
+} __attribute__((packed)) FRUSlotPosRecord;
+
+typedef struct
+{
+    uint8_t node1;
+    uint8_t node2;
+    uint8_t node3;
+    uint8_t node4;
+    uint8_t node5;
+    uint8_t node6;
+    uint8_t clock1;
+    uint8_t clock2;
+    uint8_t timeHigh1;
+    uint8_t timeHigh2;
+    uint8_t timeMid1;
+    uint8_t timeMid2;
+    uint8_t timeLow1;
+    uint8_t timeLow2;
+    uint8_t timeLow3;
+    uint8_t timeLow4;
+} __attribute__((packed)) GUIDData;
+
+typedef struct
+{
+    uint8_t biosIdLength;
+    uint8_t biosId[maxBiosIdLength];
+} __attribute__((packed)) DeviceInfo;
+
+typedef struct
+{
+    uint8_t entityType;
+    uint8_t countToRead;
+    uint8_t offset;
+} __attribute__((packed)) GetOemDeviceInfoReq;
+
+typedef struct
+{
+    uint8_t resDatalen;
+    uint8_t data[maxBiosIdLength];
+} __attribute__((packed)) GetOemDeviceInfoRes;
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
new file mode 100644
index 0000000..e6eddcc
--- /dev/null
+++ b/src/oemcommands.cpp
@@ -0,0 +1,336 @@
+/*
+// Copyright (c) 2018 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 <host-ipmid/ipmid-api.h>
+
+#include <array>
+#include <host-ipmid/utils.hpp>
+#include <iostream>
+#include <oemcommands.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace ipmi
+{
+static void register_netfn_firmware_functions() __attribute__((constructor));
+sdbusplus::bus::bus _dbus(ipmid_get_sd_bus_connection()); // from ipmid-api.h
+static constexpr size_t maxFruStringLength = 0x3F;
+
+// return code: 0 successful
+int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
+{
+    std::string objpath = "/xyz/openbmc_project/FruDevice";
+    std::string intf = "xyz.openbmc_project.FruDeviceManager";
+    std::string service = getService(bus, intf, objpath);
+    ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
+    if (valueTree.empty())
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "No object implements interface",
+            phosphor::logging::entry("INTF=%s", intf.c_str()));
+        return -1;
+    }
+
+    for (auto& item : valueTree)
+    {
+        auto interface = item.second.find("xyz.openbmc_project.FruDevice");
+        if (interface == item.second.end())
+        {
+            continue;
+        }
+
+        auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
+        if (property == interface->second.end())
+        {
+            continue;
+        }
+
+        try
+        {
+            Value variant = property->second;
+            std::string& result = variant.get<std::string>();
+            if (result.size() > maxFruStringLength)
+            {
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "FRU serial number exceed maximum length");
+
+                return -1;
+            }
+            else
+            {
+                serial = result;
+            }
+            return 0;
+        }
+        catch (mapbox::util::bad_variant_access& e)
+        {
+            std::cerr << e.what() << std::endl;
+            return -1;
+        }
+    }
+    return -1;
+}
+ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                           ipmi_request_t request, ipmi_response_t response,
+                           ipmi_data_len_t data_len, ipmi_context_t context)
+{
+    phosphor::logging::log<phosphor::logging::level::INFO>(
+        "Handling OEM WILDCARD", phosphor::logging::entry("NETFN=%x", netfn),
+        phosphor::logging::entry("CMD=%x", cmd));
+
+    // Status code.
+    ipmi_ret_t rc = IPMI_CC_INVALID;
+    *data_len = 0;
+    return rc;
+}
+
+// Returns the Chassis Identifier (serial #)
+ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                                       ipmi_request_t request,
+                                       ipmi_response_t response,
+                                       ipmi_data_len_t data_len,
+                                       ipmi_context_t context)
+{
+    std::string serial;
+    if (*data_len != 0) // invalid request if there are extra parameters
+    {
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+    if (getChassisSerialNumber(_dbus, serial) == 0)
+    {
+        *data_len = serial.size(); // length will never exceed response length
+                                   // as it is checked in getChassisSerialNumber
+        char* resp = static_cast<char*>(response);
+        serial.copy(resp, *data_len);
+        return IPMI_CC_OK;
+    }
+    else
+    {
+        *data_len = 0;
+        return IPMI_CC_RESPONSE_ERROR;
+    }
+}
+
+ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                                ipmi_request_t request,
+                                ipmi_response_t response,
+                                ipmi_data_len_t data_len,
+                                ipmi_context_t context)
+{
+    static constexpr size_t safeBufferLength = 50;
+    char buf[safeBufferLength] = {0};
+    GUIDData* Data = reinterpret_cast<GUIDData*>(request);
+
+    if (*data_len != sizeof(GUIDData)) // 16bytes
+    {
+        *data_len = 0;
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    *data_len = 0;
+
+    snprintf(
+        buf, safeBufferLength,
+        "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+        Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
+        Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
+        Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
+        Data->node3, Data->node2, Data->node1);
+    // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
+    std::string guid = buf;
+    phosphor::logging::log<phosphor::logging::level::INFO>(
+        "Set System GUID", phosphor::logging::entry("GUID=%s", guid.c_str()));
+
+    std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
+    std::string intf = "xyz.openbmc_project.Common.UUID";
+    std::string service = getService(_dbus, intf, objpath);
+    setDbusProperty(_dbus, service, objpath, intf, "UUID", guid);
+    return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                            ipmi_request_t request, ipmi_response_t response,
+                            ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+    DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
+
+    if ((*dataLen < 2) || (*dataLen != (1 + data->biosIdLength)))
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Invalid parameter", phosphor::logging::entry("LEN=%d", *dataLen),
+            phosphor::logging::entry("BIOSIDLEN=%d", data->biosIdLength));
+
+        *dataLen = 0;
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+    std::string idString((char*)data->biosId, data->biosIdLength);
+
+    std::string service = getService(_dbus, biosIntf, biosObjPath);
+    setDbusProperty(_dbus, service, biosObjPath, biosIntf, biosProp, idString);
+    uint8_t* bytesWritten = static_cast<uint8_t*>(response);
+    *bytesWritten =
+        data->biosIdLength; // how many bytes are written into storage
+    *dataLen = 1;
+    return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                                ipmi_request_t request,
+                                ipmi_response_t response,
+                                ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+    GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
+    GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
+
+    if (*dataLen == 0)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Paramter length should be at least one byte");
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    size_t reqDataLen = *dataLen;
+    *dataLen = 0;
+    if (req->entityType > OEMDevEntityType::sdrVer)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Out of range",
+            phosphor::logging::entry("TYPE=%x", req->entityType));
+
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    // handle OEM command items
+    switch (req->entityType)
+    {
+        case OEMDevEntityType::biosId:
+        {
+            if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
+            {
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "Data length does not match request",
+                    phosphor::logging::entry("REQLEN=%d", reqDataLen),
+                    phosphor::logging::entry("LEN=%d",
+                                             sizeof(GetOemDeviceInfoReq)));
+                return IPMI_CC_REQ_DATA_LEN_INVALID;
+            }
+
+            std::string service = getService(_dbus, biosIntf, biosObjPath);
+            try
+            {
+                Value variant = getDbusProperty(_dbus, service, biosObjPath,
+                                                biosIntf, biosProp);
+                std::string& idString = variant.get<std::string>();
+                if (req->offset >= idString.size())
+                {
+                    phosphor::logging::log<phosphor::logging::level::ERR>(
+                        "offset exceed range",
+                        phosphor::logging::entry("OFFSET=%d", req->offset),
+                        phosphor::logging::entry("IDLEN=%d", idString.size()));
+                    return IPMI_CC_PARM_OUT_OF_RANGE;
+                }
+                else
+                {
+                    size_t length = 0;
+                    if (req->countToRead > (idString.size() - req->offset))
+                    {
+                        length = idString.size() - req->offset;
+                    }
+                    else
+                    {
+                        length = req->countToRead;
+                    }
+                    std::copy(idString.begin() + req->offset, idString.end(),
+                              res->data);
+                    res->resDatalen = length;
+                    *dataLen = res->resDatalen + 1;
+                }
+            }
+            catch (mapbox::util::bad_variant_access& e)
+            {
+                std::cerr << e.what() << std::endl;
+                return IPMI_CC_UNSPECIFIED_ERROR;
+            }
+        }
+        break;
+
+        case OEMDevEntityType::devVer:
+        case OEMDevEntityType::sdrVer:
+            // TODO:
+            return IPMI_CC_ILLEGAL_COMMAND;
+        default:
+            return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                            ipmi_request_t request, ipmi_response_t response,
+                            ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+    if (*dataLen != 0)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "This command should not have any paramter");
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    *dataLen = 1;
+    uint8_t* res = reinterpret_cast<uint8_t*>(response);
+    // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
+    // AIC is available so that BIOS will not timeout repeatly which leads to
+    // slow booting.
+    *res = 0; // Byte1=Count of SlotPosition/FruID records.
+    return IPMI_CC_OK;
+}
+
+void ipmi_register(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_context_t context,
+                   ipmid_callback_t handler, ipmi_cmd_privilege_t priv)
+{
+    std::ostringstream os;
+    os << "Registering NetFn:[0x" << std::hex << std::uppercase << netfn
+       << "], Cmd:[0x" << cmd << "]\n";
+    phosphor::logging::log<phosphor::logging::level::INFO>(os.str().c_str());
+    ipmi_register_callback(netfn, cmd, context, handler, priv);
+}
+
+static void register_netfn_firmware_functions(void)
+{
+    phosphor::logging::log<phosphor::logging::level::INFO>(
+        "Registering OEM commands");
+    ipmi_register(netfunIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
+                  ipmiOEMWildcard,
+                  PRIVILEGE_USER); // wildcard default handler
+    ipmi_register(netfunIntcOEMGeneral, cmdGetChassisIdentifier, NULL,
+                  ipmiOEMGetChassisIdentifier,
+                  PRIVILEGE_USER); // get chassis identifier
+    ipmi_register(netfunIntcOEMGeneral, cmdSetSystemGUID, NULL,
+                  ipmiOEMSetSystemGUID,
+                  PRIVILEGE_ADMIN); // set system guid
+    ipmi_register(netfunIntcOEMGeneral, cmdSetBIOSID, NULL, ipmiOEMSetBIOSID,
+                  PRIVILEGE_ADMIN);
+    ipmi_register(netfunIntcOEMGeneral, cmdGetOEMDeviceInfo, NULL,
+                  ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
+    ipmi_register(netfunIntcOEMGeneral, cmdGetAICSlotFRUIDRecords, NULL,
+                  ipmiOEMGetAICFRU, PRIVILEGE_USER);
+    return;
+}
+
+} // namespace ipmi