blob: 58e4d0e296801e90874d7c588f49f614851dda11 [file] [log] [blame]
Johnathan Mantey5969d162023-10-26 14:31:24 -07001From 13d0b106e6512dd984bbec6e8b450731989d0618 Mon Sep 17 00:00:00 2001
Alex Schendel6a69fe42023-06-20 19:42:53 -07002From: Alex Schendel <alex.schendel@intel.com>
3Date: Fri, 9 Jun 2023 16:35:24 -0700
4Subject: [PATCH] Fru: Fix edit field not checking area existence
5
6The current implementation of ipmitool fru edit does not perform proper
7checks when attempting to resize the FRU. This results in undesireable
8changes to the FRU in several instances:
91. If the FRU is shrinking and a FRU area does not exist (offset 0),
10ipmitool may attempt to shift it forwards (decrementing the offset).
11This results in a wraparound to 0xFF, leaving an erroneous field offset.
122. If the areas are not in the exact order given as an example in the
13FRU spec, ipmitool may shift the wrong fields, which would cause data
14loss. (the FRU spec does not specify a required order for FRU fields)
153. If the FRU is being enlarged after a fru field edit, the FRU size is
16not properly modified before writing the FRU, so the end of the FRU
17becomes truncated, resulting in data loss.
18
19This commit addresses these three issues by:
201. Confirming that a area's does not have an offset of 0x00 before
21attempting to shift it.
222. Ensuring that the area's offset is after the area that was modified
23before attempting to shift it.
243. Properly edit the size of the FRU before the FRU is written.
25
26Tested:
27Shrinking a FRU was tested with and without the change:
28New Header without change:
2901 00 00 01 0a ff 00 f5
30 ^^
31Note that the Multi Record area now has an offset of 0xFF.
32
33New Header with change:
3401 00 00 01 0a 00 00 f4
35 ^^
36Note that the Multi Record area retains its offset of 0x00.
37
38This change also includes printouts specifying what offsets are found
39and when they are being shifted, as well as data being erased if the FRU
40is being shrunk:
41Offset: 0
42Offset: 0
43Offset: 8
44Offset: 88 moving by -8 bytes.
45Offset: 0
46Erasing leftover data from 200 to 208
47
48After shrinking the FRU, the FRU was reverted to its original state with
49the fix in place:
5001 00 00 01 0b 00 00 f3
51 ^^
52This resulted in only the product area offset being updated as expected.
53Offset: 0
54Offset: 0
55Offset: 8
56Offset: 80 moving by 8 bytes.
57Offset: 0
58
59The implementation of IPMI FRU write used in these tests errors out
60without writing the FRU if a checksum fails to pass, so without this
61fix, it was impossible to enlarge the FRU. This is because without the
62change, the last 8 bytes of the FRU would be truncated which would
63result in the checksum of the final FRU area being lost which would thus
64trigger this FRU write failure.
65
66Signed-off-by: Alex Schendel <alex.schendel@intel.com>
Zhikui Rena47c5b72023-09-27 14:32:44 -070067Signed-off-by: Zhikui Ren <zhikui.ren@intel.com>
Alex Schendel6a69fe42023-06-20 19:42:53 -070068---
Johnathan Mantey5969d162023-10-26 14:31:24 -070069 include/ipmitool/ipmi_fru.h | 1 +
Zhikui Rena47c5b72023-09-27 14:32:44 -070070 lib/ipmi_fru.c | 170 +++++++++++++++++++++++-------------
Johnathan Mantey5969d162023-10-26 14:31:24 -070071 2 files changed, 110 insertions(+), 61 deletions(-)
Alex Schendel6a69fe42023-06-20 19:42:53 -070072
73diff --git a/include/ipmitool/ipmi_fru.h b/include/ipmitool/ipmi_fru.h
Johnathan Mantey5969d162023-10-26 14:31:24 -070074index c6b3a54..13f2958 100644
Alex Schendel6a69fe42023-06-20 19:42:53 -070075--- a/include/ipmitool/ipmi_fru.h
76+++ b/include/ipmitool/ipmi_fru.h
Johnathan Mantey5969d162023-10-26 14:31:24 -070077@@ -46,6 +46,7 @@
Alex Schendel6a69fe42023-06-20 19:42:53 -070078 #define GET_FRU_INFO 0x10
79 #define GET_FRU_DATA 0x11
80 #define SET_FRU_DATA 0x12
81+#define FRU_AREA_COUNT 5
82
83 enum {
84 FRU_CHASSIS_PARTNO,
Alex Schendel6a69fe42023-06-20 19:42:53 -070085diff --git a/lib/ipmi_fru.c b/lib/ipmi_fru.c
Johnathan Mantey5969d162023-10-26 14:31:24 -070086index 4d1dbbb..effc1a8 100644
Alex Schendel6a69fe42023-06-20 19:42:53 -070087--- a/lib/ipmi_fru.c
88+++ b/lib/ipmi_fru.c
Johnathan Mantey5969d162023-10-26 14:31:24 -070089@@ -131,7 +131,8 @@ static int ipmi_fru_set_field_string(struct ipmi_intf * intf, unsigned
Zhikui Rena47c5b72023-09-27 14:32:44 -070090 static int
91 ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
92 struct fru_info fru, struct fru_header header,
93- uint8_t f_type, uint8_t f_index, char *f_string);
94+ uint8_t f_type, uint8_t f_index, char *f_string,
95+ int new_size);
96
97 static void
98 fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
Johnathan Mantey5969d162023-10-26 14:31:24 -070099@@ -4997,12 +4998,19 @@ f_type, uint8_t f_index, char *f_string)
Zhikui Rena47c5b72023-09-27 14:32:44 -0700100 rc = -1;
101 goto ipmi_fru_set_field_string_out;
102 }
103- }
104- else {
105- printf("String size are not equal, resizing fru to fit new string\n");
106- if(
107- ipmi_fru_set_field_string_rebuild(intf,fruId,fru,header,f_type,f_index,f_string)
108- )
109+ }
110+ else {
111+ int new_fru_size = fru.size;
112+ int change =
113+ strlen((const char *)f_string) - strlen((const char *)fru_area);
114+ if (change > 0)
115+ {
116+ /* Fru record is padded to be 8 bytes aligned */
117+ new_fru_size = fru.size + change + 8;
118+ }
119+ if (ipmi_fru_set_field_string_rebuild(intf, fruId, fru, header,
120+ f_type, f_index, f_string,
121+ new_fru_size))
122 {
123 rc = -1;
124 goto ipmi_fru_set_field_string_out;
Johnathan Mantey5969d162023-10-26 14:31:24 -0700125@@ -5048,7 +5056,8 @@ ipmi_fru_set_field_string_out:
Zhikui Rena47c5b72023-09-27 14:32:44 -0700126 static int
127 ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
128 struct fru_info fru, struct fru_header header,
129- uint8_t f_type, uint8_t f_index, char *f_string)
130+ uint8_t f_type, uint8_t f_index, char *f_string,
131+ int new_size)
132 {
133 int i = 0;
134 uint8_t *fru_data_old = NULL;
Johnathan Mantey5969d162023-10-26 14:31:24 -0700135@@ -5065,7 +5074,7 @@ ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
Zhikui Rena47c5b72023-09-27 14:32:44 -0700136
137 fru_data_old = calloc( fru.size, sizeof(uint8_t) );
138
139- fru_data_new = malloc( fru.size );
140+ fru_data_new = malloc( new_size );
141
142 if (!fru_data_old || !fru_data_new) {
143 printf("Out of memory!\n");
Johnathan Mantey5969d162023-10-26 14:31:24 -0700144@@ -5205,43 +5214,91 @@ ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
Alex Schendel6a69fe42023-06-20 19:42:53 -0700145 #endif
146
147 /* Must move sections */
148- /* Section that can be modified are as follow
149- Chassis
150- Board
151- product */
152-
153- /* Chassis type field */
154- if (f_type == 'c' )
155+ /* IPMI FRU Spec does not specify the order of areas in the FRU.
156+ * Therefore, we must check each section's current offset in order to determine
157+ * which areas much be adjusted.
158+ */
159+
160+ /* The Internal Use Area does not require the area length be provided, so we must
161+ * work to calculate the length.
162+ */
163+ bool internal_move = false;
164+ uint8_t nearest_area = fru.size;
165+ uint8_t last_area = 0x00;
166+ uint32_t end_of_fru;
167+ if (header.offset.internal != 0 && header.offset.internal > header_offset)
168 {
169- printf("Moving Section Chassis, from %i to %i\n",
170- ((header.offset.board) * 8),
171- ((header.offset.board + change_size_by_8) * 8)
172- );
173- memcpy(
174- (fru_data_new + ((header.offset.board + change_size_by_8) * 8)),
175- (fru_data_old + (header.offset.board) * 8),
176- board_len
177- );
178- header.offset.board += change_size_by_8;
179+ internal_move = true;
180 }
181- /* Board type field */
182- if ((f_type == 'c' ) || (f_type == 'b' ))
183+ /* Check Chassis, Board, Product, and Multirecord Area offsets to see if they need
184+ * to be moved.
185+ */
186+ for (int i = 0; i < FRU_AREA_COUNT; i++)
187 {
188- printf("Moving Section Product, from %i to %i\n",
189- ((header.offset.product) * 8),
190- ((header.offset.product + change_size_by_8) * 8)
191- );
192- memcpy(
193- (fru_data_new + ((header.offset.product + change_size_by_8) * 8)),
194- (fru_data_old + (header.offset.product) * 8),
195- product_len
196- );
197- header.offset.product += change_size_by_8;
198+ #ifdef DBG_RESIZE_FRU
199+ printf("Offset: %i", header.offsets[i] * 8);
200+ #endif
201+ /* Offset of zero means area does not exist.
202+ * Internal Use Area must be handled separately
203+ */
204+ if (header.offsets[i] <= 0 || header.offsets[i] == header.offset.internal)
205+ {
206+#ifdef DBG_RESIZE_FRU
207+ printf("\n");
208+#endif
209+ continue;
210+ }
211+ /* Internal Use Area length will be calculated by finding the closest area
212+ * following it.
213+ */
214+ if (internal_move && header.offsets[i] > header.offset.internal &&
215+ header.offsets[i] < nearest_area)
216+ {
217+ nearest_area = header.offsets[i];
218+ }
219+ if (last_area < header.offsets[i])
220+ {
221+ last_area = header.offsets[i];
222+ end_of_fru = (header.offsets[i] + *(fru_data_old + (header.offsets[i] * 8) + 1)) * 8;
223+ if (header.offsets[i] == header.offset.multi)
224+ {
225+ end_of_fru = (header.offsets[i] + *(fru_data_old + (header.offsets[i] * 8) + 1)) * 8;
226+ }
227+ }
228+ if ((header.offsets[i] * 8) > header_offset)
229+ {
230+#ifdef DBG_RESIZE_FRU
231+ printf(" moving by %i bytes.", change_size_by_8 * 8);
232+#endif
233+ uint32_t length = *(fru_data_old + (header.offsets[i] * 8) + 1) * 8;
234+ /* MultiRecord Area length is third byte rather than second. */
235+ if(header.offsets[i] == header.offset.multi)
236+ {
237+ length = *(fru_data_old + (header.offsets[i] * 8) + 2) * 8;
238+ }
239+ memcpy(
240+ (fru_data_new + ((header.offsets[i] + change_size_by_8) * 8)),
241+ (fru_data_old + (header.offsets[i]) * 8),
242+ length
243+ );
244+ header.offsets[i] += change_size_by_8;
245+ }
246+#ifdef DBG_RESIZE_FRU
247+ printf("\n");
248+#endif
249 }
250-
251- if ((f_type == 'c' ) || (f_type == 'b' ) || (f_type == 'p' )) {
252- printf("Change multi offset from %d to %d\n", header.offset.multi, header.offset.multi + change_size_by_8);
253- header.offset.multi += change_size_by_8;
254+ if (internal_move)
255+ {
256+ /* If the internal area is the final area in the FRU, then the only bearing
257+ * we have for the length of the FRU is the size of the FRU.
258+ */
259+ uint32_t length = nearest_area - header.offset.internal;
260+ memcpy(
261+ (fru_data_new + ((header.offset.internal + change_size_by_8) * 8)),
262+ (fru_data_old + (header.offset.internal) * 8),
263+ length
264+ );
265+ header.offset.internal += change_size_by_8;
266 }
267
268 /* Adjust length of the section */
Johnathan Mantey5969d162023-10-26 14:31:24 -0700269@@ -5271,27 +5328,18 @@ ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
Alex Schendel6a69fe42023-06-20 19:42:53 -0700270 memcpy(fru_data_new, pfru_header, sizeof(struct fru_header));
271 }
272
273- /* Move remaining sections in 1 copy */
274- printf("Moving Remaining Bytes (Multi-Rec , etc..), from %i to %i\n",
275- remaining_offset,
276- ((header.offset.product) * 8) + product_len_new
277- );
278- if(((header.offset.product * 8) + product_len_new - remaining_offset) < 0)
Zhikui Rena47c5b72023-09-27 14:32:44 -0700279+ /* If FRU has shrunk in size, zero-out any leftover data */
280+ if (change_size_by_8 < 0)
281 {
Alex Schendel6a69fe42023-06-20 19:42:53 -0700282- memcpy(
283- fru_data_new + (header.offset.product * 8) + product_len_new,
284- fru_data_old + remaining_offset,
285- fru.size - remaining_offset
286- );
287- }
288- else
Zhikui Rena47c5b72023-09-27 14:32:44 -0700289- {
Alex Schendel6a69fe42023-06-20 19:42:53 -0700290- memcpy(
291- fru_data_new + (header.offset.product * 8) + product_len_new,
292- fru_data_old + remaining_offset,
293- fru.size - ((header.offset.product * 8) + product_len_new)
294- );
295+ end_of_fru += change_size_by_8 * 8;
296+ int length_of_erase = change_size_by_8 * -1 * 8;
297+#ifdef DBG_RESIZE_FRU
298+ printf("Erasing leftover data from %i to %i\n", end_of_fru, end_of_fru + length_of_erase);
299+#endif
300+ memset(fru_data_new + end_of_fru, 0, length_of_erase);
301 }
302+ /* Step 7 assumes fru.size is the size of the new FRU. */
303+ fru.size += (change_size_by_8 * 8);
304 }
305
306 /* Update only if it's fits padding length as defined in the spec, otherwise, it's an internal
307--
Johnathan Mantey5969d162023-10-26 14:31:24 -07003082.41.0
Alex Schendel6a69fe42023-06-20 19:42:53 -0700309