storagecommands: Split validation logic
The Fru validation logic is useful in isolation, split it into a
separate library that can be included.
Tested:
[0/1] Running all tests.
1/12 phosphor-objmgr / well_known OK 0.24s
2/12 phosphor-objmgr / need_to_introspect OK 0.22s
3/12 phosphor-objmgr / name_change OK 0.16s
4/12 phosphor-objmgr / interfaces_added OK 0.13s
5/12 phosphor-objmgr / handler OK 0.09s
6/12 phosphor-objmgr / mapper OK 0.07s
7/12 phosphor-host-ipmid / entitymap_json OK 0.06s
8/12 phosphor-host-ipmid / message OK 0.05s
9/12 phosphor-host-ipmid / session/closesession OK 0.03s
10/12 phosphor-objmgr / associations OK 0.20s
11/12 phosphor-host-ipmid / dbus-sdr/sensorcommands OK 0.02s
12/12 intel-ipmi-oem / message OK 0.02s
Ok: 12
Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
Change-Id: I9130eb81703b0cda7c3229f16cd689dd2c96c55c
Signed-off-by: Peter Foley <pefoley@google.com>
diff --git a/include/fruutils.hpp b/include/fruutils.hpp
new file mode 100644
index 0000000..f830b71
--- /dev/null
+++ b/include/fruutils.hpp
@@ -0,0 +1,24 @@
+/*
+// Copyright (c) 2022-2023 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
+
+#include <cstdint>
+#include <vector>
+
+// Validate the vector holds a complete FRU's contents.
+bool validateBasicFruContent(const std::vector<uint8_t>& fru,
+ size_t lastWriteAddr);
diff --git a/include/storagecommands.hpp b/include/storagecommands.hpp
index 865427f..c359702 100644
--- a/include/storagecommands.hpp
+++ b/include/storagecommands.hpp
@@ -252,20 +252,6 @@
};
#pragma pack(push, 1)
-struct FRUHeader
-{
- uint8_t commonHeaderFormat;
- uint8_t internalOffset;
- uint8_t chassisOffset;
- uint8_t boardOffset;
- uint8_t productOffset;
- uint8_t multiRecordOffset;
- uint8_t pad;
- uint8_t checksum;
-};
-#pragma pack(pop)
-
-#pragma pack(push, 1)
struct Type12Record
{
get_sdr::SensorDataRecordHeader header;
diff --git a/meson.build b/meson.build
index 2f01792..7383ce8 100644
--- a/meson.build
+++ b/meson.build
@@ -111,6 +111,7 @@
'src/me_to_redfish_hooks.cpp',
'src/chassiscommands.cpp',
'src/allowlist-filter.cpp',
+ 'src/fruutils.cpp',
ipmiallowlist,
]
diff --git a/src/fruutils.cpp b/src/fruutils.cpp
new file mode 100644
index 0000000..625ca91
--- /dev/null
+++ b/src/fruutils.cpp
@@ -0,0 +1,91 @@
+/*
+// Copyright (c) 2022-2023 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 "fruutils.hpp"
+
+#include <algorithm>
+#include <vector>
+
+#pragma pack(push, 1)
+struct FRUHeader
+{
+ uint8_t commonHeaderFormat;
+ uint8_t internalOffset;
+ uint8_t chassisOffset;
+ uint8_t boardOffset;
+ uint8_t productOffset;
+ uint8_t multiRecordOffset;
+ uint8_t pad;
+ uint8_t checksum;
+};
+#pragma pack(pop)
+
+bool validateBasicFruContent(const std::vector<uint8_t>& fru,
+ size_t lastWriteAddr)
+{
+ bool atEnd = false;
+
+ if (fru.size() >= sizeof(FRUHeader))
+ {
+ const FRUHeader* header =
+ reinterpret_cast<const FRUHeader*>(fru.data());
+
+ int areaLength = 0;
+ size_t lastRecordStart = std::max(
+ {header->internalOffset, header->chassisOffset, header->boardOffset,
+ header->productOffset, header->multiRecordOffset});
+ lastRecordStart *= 8; // header starts in are multiples of 8 bytes
+
+ if (header->multiRecordOffset)
+ {
+ // This FRU has a MultiRecord Area
+ uint8_t endOfList = 0;
+ // Walk the MultiRecord headers until the last record
+ while (!endOfList)
+ {
+ // The MSB in the second byte of the MultiRecord header signals
+ // "End of list"
+ endOfList = fru[lastRecordStart + 1] & 0x80;
+ // Third byte in the MultiRecord header is the length
+ areaLength = fru[lastRecordStart + 2];
+ // This length is in bytes (not 8 bytes like other headers)
+ areaLength += 5; // The length omits the 5 byte header
+ if (!endOfList)
+ {
+ // Next MultiRecord header
+ lastRecordStart += areaLength;
+ }
+ }
+ }
+ else
+ {
+ // This FRU does not have a MultiRecord Area
+ // Get the length of the area in multiples of 8 bytes
+ if (lastWriteAddr > (lastRecordStart + 1))
+ {
+ // second byte in record area is the length
+ areaLength = fru[lastRecordStart + 1];
+ areaLength *= 8; // it is in multiples of 8 bytes
+ }
+ }
+ if (lastWriteAddr >= (areaLength + lastRecordStart))
+ {
+ atEnd = true;
+ }
+ }
+
+ return atEnd;
+}
diff --git a/src/storagecommands.cpp b/src/storagecommands.cpp
index 8c9b49a..5f1a8f8 100644
--- a/src/storagecommands.cpp
+++ b/src/storagecommands.cpp
@@ -17,6 +17,7 @@
#include "storagecommands.hpp"
#include "commandutils.hpp"
+#include "fruutils.hpp"
#include "ipmi_to_redfish_hooks.hpp"
#include "sdrutils.hpp"
#include "types.hpp"
@@ -523,55 +524,7 @@
std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen,
fruCache.begin() + fruInventoryOffset);
- bool atEnd = false;
-
- if (fruCache.size() >= sizeof(FRUHeader))
- {
- FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
-
- int areaLength = 0;
- size_t lastRecordStart = std::max(
- {header->internalOffset, header->chassisOffset, header->boardOffset,
- header->productOffset, header->multiRecordOffset});
- lastRecordStart *= 8; // header starts in are multiples of 8 bytes
-
- if (header->multiRecordOffset)
- {
- // This FRU has a MultiRecord Area
- uint8_t endOfList = 0;
- // Walk the MultiRecord headers until the last record
- while (!endOfList)
- {
- // The MSB in the second byte of the MultiRecord header signals
- // "End of list"
- endOfList = fruCache[lastRecordStart + 1] & 0x80;
- // Third byte in the MultiRecord header is the length
- areaLength = fruCache[lastRecordStart + 2];
- // This length is in bytes (not 8 bytes like other headers)
- areaLength += 5; // The length omits the 5 byte header
- if (!endOfList)
- {
- // Next MultiRecord header
- lastRecordStart += areaLength;
- }
- }
- }
- else
- {
- // This FRU does not have a MultiRecord Area
- // Get the length of the area in multiples of 8 bytes
- if (lastWriteAddr > (lastRecordStart + 1))
- {
- // second byte in record area is the length
- areaLength = fruCache[lastRecordStart + 1];
- areaLength *= 8; // it is in multiples of 8 bytes
- }
- }
- if (lastWriteAddr >= (areaLength + lastRecordStart))
- {
- atEnd = true;
- }
- }
+ bool atEnd = validateBasicFruContent(fruCache, lastWriteAddr);
uint8_t countWritten = 0;
writeBus = cacheBus;