Add boot count into motherboard eeprom and clear command

The bmc boot count which was previously stored into
the file /etc/conf/bios.cnt, is now stored into the
motherboard FRU eeprom/sys/bus/i2c/devices/4-0050/eeprom
starting from offset of 4 bytes from 0x1000.

The reading and writing to the FRU eeprom is handled
by using the helper functions.

Added the new IPMI clear command which sets the eeprom
to the default value of ff ff ff ff.

Use of 4 bytes from 0x1000 location to check
if the boot counter is at the max value or at the default
value which server as a header to the boot count.

All corner cases such as invalid command and invalid
command length have been added

Tested:
ipmitool raw 0x34 0x71 0x00 - read current boot count.
ipmitool raw 0x34 0x71 0x01 - increment the boot count by 1.
ipmitool raw 0x34 0x71 0x02 - set the boot_count to all ff which
is its default value of eeprom location where the boot count is
stored.
ipmitool raw 0x34 0x71 0x03 byte1 byte2 byte3 byte4 - set the
boot count to desired value provided by the request parameter.
Reboot,powercyle,flashing bmc - performed to see if the value
retains.

Signed-off-by: Avenash Asai Thambi <avenash.thambi@fii-usa.com>
Change-Id: I99dd5e6ba1d943e558e984d948bd63552cd5278f
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f5cf270..da3ff73 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -83,6 +83,7 @@
 add_library (fiioemcmds
              SHARED
              src/systemcommands.cpp
+             src/file_handling.cpp
              src/bioscommands.cpp)
 
 set_target_properties (fiioemcmds PROPERTIES VERSION "0.1.0")
diff --git a/README.md b/README.md
index 76919c3..8764e81 100644
--- a/README.md
+++ b/README.md
@@ -28,37 +28,47 @@
 |--------|-------|----
 |1|Read the information|ipmitool 0x34 0x03
 
-### BIOS Related Commands (7x h)
+### BIOS Related Commands (0x7X)
 
 #### BIOS Boot Count 0x71
 
-This is a 32 bits register that provides bios pushs the boot counter to BMC, or 
+This is a 32 bits register that provides bios pushs the boot counter to BMC, or
 get the boot counter from BMC.
 
 Request
+|Byte(s) |Value|Description
+|--------|-----|----
+|0x00|0x71|Subcommand
+|0x01|Op value|Operation to be performed
+|0x02-0x05|byte0 byte1 byte2 byte3|32-bit Count value for set operation
 
-|Byte(s) |Value  |Data
-|--------|-------|----
-|0x00|0x03|Subcommand
-|0x01|0x00/0x01| read/write the boot counter register
-|0x02|0x--|Byte 0
-|0x03|0x--|Byte 1
-|0x04|0x--|Byte 2
-|0x05|0x--|Byte 3
+|Op value |Operation to be performed
+|------|---------
+|0x00|Read boot count value
+|0x01|Increment boot count value
+|0x02|Clear boot count value
+|0x03|Set the boot count with given 4 byte value
 
 Response
-
-|Byte(s) |Value  |Data
+|Byte(s) |Value  |Description
 |--------|-------|----
-|0x00|0x71|Subcommand
-|0x00|0x--|Byte 0
-|0x01|0x--|Byte 1
-|0x02|0x--|Byte 2
-|0x03|0x--|Byte 3
+|0x00|CC|Completion code returned
+|0x01|--|Byte 0 of boot count
+|0x02|--|Byte 1 of boot count
+|0x03|--|Byte 2 of boot count
+|0x04|--|Byte 3 of boot count
+
+Completion Codes (CC)
+|CC   |Description
+|-----|-----------|
+|0xC1|Invalid Command
+|0x00|Command Success
+|0xC7|Data length Invalid
 
 Example
 |# |Command |Example
 |--------|-------|----
 |1|Read the boot count|ipmitool raw 0x34 0x71 0x00
-|2|Add the boot count by 1|ipmitool raw 0x34 0x71 0x01
-|3|Add the boot count by index|ipmitool raw 0x34 0x71 0x01 byte0 byte1 byte2 byte3
+|2|Increment the boot count by 1|ipmitool raw 0x34 0x71 0x01
+|3|Clear the boot count to all 0's|ipmitool raw 0x34 0x71 0x02
+|4|Set the boot count to given value|ipmitool raw 0x34 0x71 0x03 byte0 byte1 byte2 byte3
diff --git a/include/bioscommands.hpp b/include/bioscommands.hpp
index 15b6c13..6a705a5 100644
--- a/include/bioscommands.hpp
+++ b/include/bioscommands.hpp
@@ -15,12 +15,16 @@
 ********************************************************************************/
 
 #pragma once
+#define BOOT_COUNT_READ    0x00
+#define BOOT_COUNT_INCREMENT   0x01
+#define BOOT_COUNT_CLEAR   0x02
+#define BOOT_COUNT_SET   0x03
+#define FII_CMD_BIOS_BOOT_COUNT 0x71
+#define OPERATION_BYTE_LENGTH 1
+#define SET_BYTE_LENGTH 5
+#define BOOT_COUNT_HEADER 0xDEADBEEF
+#define INITIAL_VALUE 0x00000000
+#define START_BOOT_COUNT_VALUE 0x00000001
 
-#define BOOT_COUNT_FILE "/etc/conf/bios.cnt"
-
-enum fii_bios_cmds
-{
-    FII_CMD_BIOS_BOOT_COUNT = 0x71,
-};
-
-#define FII_CMD_BIOS_BOOT_COUNT_LEN 5
+size_t EEPROM_OFFSET = 4096;
+std::string EEPROM_PATH = "/sys/bus/i2c/devices/4-0050/eeprom";
diff --git a/include/common.hpp b/include/file_handling.hpp
similarity index 75%
rename from include/common.hpp
rename to include/file_handling.hpp
index c2d64a9..bb4f703 100644
--- a/include/common.hpp
+++ b/include/file_handling.hpp
@@ -1,7 +1,7 @@
 /********************************************************************************
 *                       HON HAI Precision IND.Co., LTD.                         *
 *            Personal Computer & Enterprise Product Business Group              *
-*                      Enterprise Product Business Gro:qup                      *
+*                      Enterprise Product Business Group                        *
 *                                                                               *
 *     Copyright (c) 2010 by FOXCONN/CESBG/CABG/SRD. All rights reserved.        *
 *     All data and information contained in this document is confidential       *
@@ -13,20 +13,17 @@
 *     permission of FOXCONN/CESBG/CABG/SRD.                                     *
 *                                                                               *
 ********************************************************************************/
-#include <sys/stat.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <iostream>
-#include <iomanip>
-#include <sstream>
-#include <fstream>
 
-#include <ipmid/api.hpp>
-#include <ipmid/utils.hpp>
-#include <ipmid/message.hpp>
-#include <phosphor-logging/log.hpp>
-#include <sdbusplus/message/types.hpp>
+std::system_error errnoException(const std::string& message);
 
-#define OP_CODE_READ	0b00
-#define OP_CODE_WRITE	0b01
+int sysopen(const std::string& path);
+
+void sysclose(int fd_);
+
+void lseeker(int fd_, size_t offset);
+
+void readBin(int fd_, size_t offset, void *ptr, size_t size);
+
+void writeBin(int fd_, size_t offset, void *ptr, size_t size);
 
diff --git a/src/bioscommands.cpp b/src/bioscommands.cpp
index 71fc2ae..eb7fa08 100644
--- a/src/bioscommands.cpp
+++ b/src/bioscommands.cpp
@@ -14,97 +14,89 @@
 *                                                                               *
 ********************************************************************************/
 
-#include <common.hpp>
+#include <ipmid/api.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/message/types.hpp>
 #include <bioscommands.hpp>
+#include <boost/endian/arithmetic.hpp>
+#include <file_handling.hpp>
+
+struct bios_boot_count
+{
+    uint32_t header;
+    uint32_t count;
+};
 
 namespace ipmi
 {
-    static void registerBIOSFunctions() __attribute__((constructor));
-
-    ipmi::RspType<std::vector<uint8_t>> FiiBIOSBootCount(boost::asio::yield_context yield, std::vector<uint8_t> reqParams)
+static void registerBIOSFunctions() __attribute__((constructor));
+ipmi::RspType<uint32_t> FiiBIOSBootCount(boost::asio::yield_context yield,
+        std::vector<uint8_t> reqParams)
+{
+    int boot_count_operation;
+    bios_boot_count boot;
+    if (reqParams.empty())
     {
-        bool op;
-        std::vector<uint8_t> boot_count;
-        uint32_t counter = 0, ret;
-
-        if (reqParams.empty())
-        {
-            phosphor::logging::log<phosphor::logging::level::ERR>(" Fii bios cmd : command format error.");
-
-            return ipmi::responseReqDataLenInvalid();
-        }
-
-        op = reqParams[0] & 0b11;
-        // check the boot count file exist or not
-        std::fstream fptr(BOOT_COUNT_FILE);
-
-        if (!fptr.is_open())
-        {
-            std::cerr << " Fii bios cmd : file didn't exist and try to create one\n";
-            ret = system("mkdir -p /etc/conf");
-            std::ofstream outfile (BOOT_COUNT_FILE);
-            outfile << "0" << std::endl;
-            outfile.close();
-            boot_count.push_back(static_cast<uint8_t>(counter));
-            boot_count.push_back(static_cast<uint8_t>(counter >> 8));
-            boot_count.push_back(static_cast<uint8_t>(counter >> 16));
-            boot_count.push_back(static_cast<uint8_t>(counter >> 24));
-        }
-        else
-        {
-            std::string str;
-            while (std::getline(fptr, str))
-            {
-                //boot_count.push_back(static_cast<uint8_t>(std::stoul(str)));
-                counter = (std::stoul(str));
-                //std::cerr << " Fii bios cmd : " << counter << std::endl;
-            }
-            boot_count.push_back(static_cast<uint8_t>(counter));
-            boot_count.push_back(static_cast<uint8_t>(counter >> 8));
-            boot_count.push_back(static_cast<uint8_t>(counter >> 16));
-            boot_count.push_back(static_cast<uint8_t>(counter >> 24));
-            fptr.close();
-        }
-        if (op == OP_CODE_READ)
-        {
-            return ipmi::responseSuccess(boot_count);
-        }
-        else if (op == OP_CODE_WRITE)
-        {
-            uint32_t value = 0;
-            if (reqParams.size() == 1)
-            {
-                value = boot_count[0] + (boot_count[1] << 8) + (boot_count[2] << 16) + (boot_count[3] << 24);
-                value += 1;
-                boot_count.clear();
-                boot_count.push_back(static_cast<uint8_t>(value));
-                boot_count.push_back(static_cast<uint8_t>(value >> 8));
-                boot_count.push_back(static_cast<uint8_t>(value >> 16));
-                boot_count.push_back(static_cast<uint8_t>(value >> 24));
-            }
-            else if (reqParams.size() == FII_CMD_BIOS_BOOT_COUNT_LEN)
-            {
-                value = reqParams[1] + + (reqParams[2] << 8) + (reqParams[3] << 16) + (reqParams[4] << 24);
-                boot_count.clear();
-                boot_count.insert(boot_count.begin(), reqParams.begin()+1, reqParams.end());
-            }
-            std::ofstream fptr_w(BOOT_COUNT_FILE, std::ios::out | std::ios::trunc);
-            fptr_w << value << std::endl;
-            fptr_w.close();
-        }
-        else
-        {
-            return ipmi::responseInvalidCommand();
-        }
-        return ipmi::responseSuccess(boot_count);
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+                " Fii bios cmd : command format error.");
+        return ipmi::responseReqDataLenInvalid();
     }
 
-    void registerBIOSFunctions()
-    {
-        std::fprintf(stderr, "Registering OEM:[0x34], Cmd:[%#04X] for Fii BIOS OEM Commands\n", FII_CMD_BIOS_BOOT_COUNT);
-        ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnOemThree, FII_CMD_BIOS_BOOT_COUNT, ipmi::Privilege::User,
-                FiiBIOSBootCount);
+    boot_count_operation = reqParams[0];
 
-        return;
+    if((boot_count_operation == BOOT_COUNT_SET &&
+        reqParams.size() != SET_BYTE_LENGTH) ||
+        (boot_count_operation != BOOT_COUNT_SET &&
+         reqParams.size() != OPERATION_BYTE_LENGTH))
+    {
+         return ipmi::responseReqDataLenInvalid();
     }
+
+    if(boot_count_operation > BOOT_COUNT_SET)
+    {
+         return ipmi::responseInvalidCommand();
+    }
+
+    int fd = sysopen(EEPROM_PATH);
+    readBin(fd, EEPROM_OFFSET, &boot, sizeof(boot));
+
+    if(boot.header != BOOT_COUNT_HEADER)
+    {
+        phosphor::logging::log<phosphor::logging::level::INFO>(
+                  "Boot count header is corrupted or missing. Initializing.");
+        boot.header = BOOT_COUNT_HEADER;
+        boot.count = INITIAL_VALUE;
+        writeBin(fd, EEPROM_OFFSET, &boot, sizeof(boot));
+    }
+
+    switch(boot_count_operation)
+    {
+        case BOOT_COUNT_READ:
+            break;
+        case BOOT_COUNT_INCREMENT:
+            boot.count = boot.count + 1;
+            break;
+        case BOOT_COUNT_CLEAR:
+            boot.count = INITIAL_VALUE;
+            break;
+        case BOOT_COUNT_SET:
+            memcpy(&boot.count, &reqParams[1], sizeof(boot.count));
+            break;
+    }
+
+    if( boot_count_operation != BOOT_COUNT_READ )
+    {
+        writeBin(fd, EEPROM_OFFSET + 4, &boot.count, sizeof(boot.count));
+    } 
+    sysclose(fd);
+    return ipmi::responseSuccess(boot.count);
+}
+
+void registerBIOSFunctions()
+{
+    std::fprintf(stderr, "Registering OEM:[0x34], Cmd:[%#04X] for Fii BIOS OEM Commands\n",
+            FII_CMD_BIOS_BOOT_COUNT);
+    ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnOemThree, FII_CMD_BIOS_BOOT_COUNT,
+            ipmi::Privilege::User, FiiBIOSBootCount);
+}
 }
diff --git a/src/file_handling.cpp b/src/file_handling.cpp
new file mode 100644
index 0000000..529e9f2
--- /dev/null
+++ b/src/file_handling.cpp
@@ -0,0 +1,59 @@
+#include <iostream>
+#include <fcntl.h>
+#include <unistd.h>
+
+using namespace std::string_literals;
+std::system_error errnoException(const std::string& message)
+{
+    return std::system_error(errno, std::generic_category(), message);
+}
+
+int sysopen(const std::string& path)
+{
+    int fd_ = open(path.c_str(), O_RDWR);
+    if (fd_ < 0)
+    {
+        throw errnoException("Error opening file "s + path);
+    }
+    return fd_;
+}
+
+void sysclose(int fd_)
+{
+    close(fd_);
+}
+
+void lseeker(int fd_, size_t offset)
+{
+    if (lseek(fd_, offset, SEEK_SET) < 0)
+    {
+        throw errnoException("Cannot lseek to pos "s + std::to_string(offset));
+    }
+}
+
+void readBin(int fd_, size_t offset, void *ptr, size_t size)
+{
+    lseeker(fd_, offset);
+    size_t ret = read(fd_, ptr, size);
+    if(ret < 0 )
+    {
+        throw errnoException("Error reading from file"s);
+    }
+}
+
+void writeBin(int fd_, size_t offset, void *ptr, size_t size)
+{
+    lseeker(fd_, offset);
+    ssize_t ret;
+    ret = write(fd_, ptr, size);
+    if (ret < 0)
+    {
+        throw errnoException("Error writing to file"s);
+    }
+    if (static_cast<size_t>(ret) != size)
+    {
+        throw std::runtime_error(
+                "Tried to send data size "s + std::to_string(size) +
+                " but could only send "s + std::to_string(ret));
+    }
+}
diff --git a/src/systemcommands.cpp b/src/systemcommands.cpp
index f0d5887..3cfb574 100644
--- a/src/systemcommands.cpp
+++ b/src/systemcommands.cpp
@@ -14,10 +14,11 @@
 *                                                                               *
 ********************************************************************************/
 
-#include <common.hpp>
-#include <systemcommands.hpp>
 
-#include <stdio.h>
+#include <systemcommands.hpp>
+#include <ipmid/api.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/message/types.hpp>
 
 namespace ipmi
     {