1 /*****************************************************************************\
2  *  $Id: ipmi-sdr-cache-create.c,v 1.42 2010-02-08 22:09:40 chu11 Exp $
3  *****************************************************************************
4  *  Copyright (C) 2007-2015 Lawrence Livermore National Security, LLC.
5  *  Copyright (C) 2006-2007 The Regents of the University of California.
6  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
7  *  Written by Albert Chu <chu11@llnl.gov>
8  *  UCRL-CODE-222073
9  *
10  *  This file is part of Ipmimonitoring, an IPMI sensor monitoring
11  *  library.  For details, see http://www.llnl.gov/linux/.
12  *
13  *  Ipmimonitoring is free software; you can redistribute it and/or modify
14  *  it under the terms of the GNU General Public License as published by the
15  *  Free Software Foundation; either version 3 of the License, or (at your
16  *  option) any later version.
17  *
18  *  Ipmimonitoring is distributed in the hope that it will be useful, but
19  *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20  *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21  *  for more details.
22  *
23  *  You should have received a copy of the GNU General Public License along
24  *  with Ipmimonitoring.  If not, see <http://www.gnu.org/licenses/>.
25 \*****************************************************************************/
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif /* HAVE_CONFIG_H */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #if STDC_HEADERS
34 #include <string.h>
35 #endif /* STDC_HEADERS */
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #if HAVE_FCNTL_H
39 #include <fcntl.h>
40 #endif /* HAVE_FCNTL_H */
41 #if HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif /* HAVE_UNISTD_H */
44 #include <assert.h>
45 #include <errno.h>
46 
47 #include "freeipmi/sdr/ipmi-sdr.h"
48 #include "freeipmi/api/ipmi-sdr-repository-cmds-api.h"
49 #include "freeipmi/cmds/ipmi-sdr-repository-cmds.h"
50 #include "freeipmi/fiid/fiid.h"
51 #include "freeipmi/debug/ipmi-debug.h"
52 #include "freeipmi/record-format/ipmi-sdr-record-format.h"
53 #include "freeipmi/spec/ipmi-comp-code-spec.h"
54 #include "freeipmi/util/ipmi-util.h"
55 
56 #include "ipmi-sdr-common.h"
57 #include "ipmi-sdr-defs.h"
58 #include "ipmi-sdr-trace.h"
59 #include "ipmi-sdr-util.h"
60 
61 #include "libcommon/ipmi-fiid-util.h"
62 
63 #include "freeipmi-portability.h"
64 #include "debug-util.h"
65 #include "fd.h"
66 
67 #define IPMI_SDR_CACHE_MAX_RESERVATION_ID_RETRY 4
68 
69 /* achu: bytes to read start = 16 specifically chosen to 16 because it
70  * appears most motherboards can handle 16.  Many cannot handle larger
71  * numbers like 32.
72  */
73 #define IPMI_SDR_CACHE_BYTES_TO_READ_START      16
74 #define IPMI_SDR_CACHE_BYTES_TO_READ_DECREMENT  4
75 
76 static int
_sdr_cache_header_write(ipmi_sdr_ctx_t ctx,ipmi_ctx_t ipmi_ctx,int fd,unsigned int * total_bytes_written,uint8_t sdr_version,uint16_t record_count,uint32_t most_recent_addition_timestamp,uint32_t most_recent_erase_timestamp)77 _sdr_cache_header_write (ipmi_sdr_ctx_t ctx,
78                          ipmi_ctx_t ipmi_ctx,
79                          int fd,
80                          unsigned int *total_bytes_written,
81                          uint8_t sdr_version,
82                          uint16_t record_count,
83                          uint32_t most_recent_addition_timestamp,
84                          uint32_t most_recent_erase_timestamp)
85 {
86   char sdr_cache_magic_buf[4];
87   char sdr_cache_version_buf[4];
88   char record_count_buf[2];
89   char most_recent_addition_timestamp_buf[4];
90   char most_recent_erase_timestamp_buf[4];
91   uint8_t header_checksum_buf[512];
92   unsigned int header_checksum_buf_len = 0;
93   uint8_t header_checksum;
94   ssize_t n;
95 
96   assert (ctx);
97   assert (ctx->magic == IPMI_SDR_CTX_MAGIC);
98   assert (ipmi_ctx);
99   assert (fd);
100   assert (total_bytes_written);
101 
102   sdr_cache_magic_buf[0] = IPMI_SDR_CACHE_FILE_MAGIC_0;
103   sdr_cache_magic_buf[1] = IPMI_SDR_CACHE_FILE_MAGIC_1;
104   sdr_cache_magic_buf[2] = IPMI_SDR_CACHE_FILE_MAGIC_2;
105   sdr_cache_magic_buf[3] = IPMI_SDR_CACHE_FILE_MAGIC_3;
106 
107   if ((n = fd_write_n (fd, sdr_cache_magic_buf, 4)) < 0)
108     {
109       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
110       return (-1);
111     }
112   if (n != 4)
113     {
114       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
115       return (-1);
116     }
117   (*total_bytes_written) += 4;
118 
119   memcpy(&header_checksum_buf[header_checksum_buf_len], sdr_cache_magic_buf, 4);
120   header_checksum_buf_len += 4;
121 
122   sdr_cache_version_buf[0] = IPMI_SDR_CACHE_FILE_VERSION_1_2_0;
123   sdr_cache_version_buf[1] = IPMI_SDR_CACHE_FILE_VERSION_1_2_1;
124   sdr_cache_version_buf[2] = IPMI_SDR_CACHE_FILE_VERSION_1_2_2;
125   sdr_cache_version_buf[3] = IPMI_SDR_CACHE_FILE_VERSION_1_2_3;
126 
127   if ((n = fd_write_n (fd, sdr_cache_version_buf, 4)) < 0)
128     {
129       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
130       return (-1);
131     }
132   if (n != 4)
133     {
134       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
135       return (-1);
136     }
137   (*total_bytes_written) += 4;
138 
139   memcpy(&header_checksum_buf[header_checksum_buf_len], sdr_cache_version_buf, 4);
140   header_checksum_buf_len += 4;
141 
142   if ((n = fd_write_n (fd, (char *)&sdr_version, 1)) < 0)
143     {
144       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
145       return (-1);
146     }
147   if (n != 1)
148     {
149       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
150       return (-1);
151     }
152   (*total_bytes_written) += 1;
153 
154   memcpy(&header_checksum_buf[header_checksum_buf_len], &sdr_version, 1);
155   header_checksum_buf_len += 1;
156 
157   /* Store record count little-endian */
158   record_count_buf[0] = (record_count & 0x00FF);
159   record_count_buf[1] = (record_count & 0xFF00) >> 8;
160 
161   if ((n = fd_write_n (fd, record_count_buf, 2)) < 0)
162     {
163       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
164       return (-1);
165     }
166   if (n != 2)
167     {
168       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
169       return (-1);
170     }
171   (*total_bytes_written) += 2;
172 
173   memcpy(&header_checksum_buf[header_checksum_buf_len], record_count_buf, 2);
174   header_checksum_buf_len += 2;
175 
176   /* Store most recent addition timestamp little-endian */
177   most_recent_addition_timestamp_buf[0] = (most_recent_addition_timestamp & 0x000000FF);
178   most_recent_addition_timestamp_buf[1] = (most_recent_addition_timestamp & 0x0000FF00) >> 8;
179   most_recent_addition_timestamp_buf[2] = (most_recent_addition_timestamp & 0x00FF0000) >> 16;
180   most_recent_addition_timestamp_buf[3] = (most_recent_addition_timestamp & 0xFF000000) >> 24;
181 
182   if ((n = fd_write_n (fd, most_recent_addition_timestamp_buf, 4)) < 0)
183     {
184       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
185       return (-1);
186     }
187   if (n != 4)
188     {
189       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
190       return (-1);
191     }
192   (*total_bytes_written) += 4;
193 
194   memcpy(&header_checksum_buf[header_checksum_buf_len], most_recent_addition_timestamp_buf, 4);
195   header_checksum_buf_len += 4;
196 
197   /* Store most recent erase timestamp little-endian */
198   most_recent_erase_timestamp_buf[0] = (most_recent_erase_timestamp & 0x000000FF);
199   most_recent_erase_timestamp_buf[1] = (most_recent_erase_timestamp & 0x0000FF00) >> 8;
200   most_recent_erase_timestamp_buf[2] = (most_recent_erase_timestamp & 0x00FF0000) >> 16;
201   most_recent_erase_timestamp_buf[3] = (most_recent_erase_timestamp & 0xFF000000) >> 24;
202 
203   if ((n = fd_write_n (fd, most_recent_erase_timestamp_buf, 4)) < 0)
204     {
205       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
206       return (-1);
207     }
208   if (n != 4)
209     {
210       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
211       return (-1);
212     }
213   (*total_bytes_written) += 4;
214 
215   memcpy(&header_checksum_buf[header_checksum_buf_len], most_recent_erase_timestamp_buf, 4);
216   header_checksum_buf_len += 4;
217 
218   header_checksum = ipmi_checksum (header_checksum_buf, header_checksum_buf_len);
219 
220   if ((n = fd_write_n (fd, (char *)&header_checksum, 1)) < 0)
221     {
222       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
223       return (-1);
224     }
225   if (n != 1)
226     {
227       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
228       return (-1);
229     }
230   (*total_bytes_written) += 1;
231 
232   return (0);
233 }
234 
235 static int
_sdr_cache_trailer_write(ipmi_sdr_ctx_t ctx,ipmi_ctx_t ipmi_ctx,int fd,unsigned int total_bytes_written,uint8_t trailer_checksum)236 _sdr_cache_trailer_write (ipmi_sdr_ctx_t ctx,
237                           ipmi_ctx_t ipmi_ctx,
238                           int fd,
239                           unsigned int total_bytes_written,
240                           uint8_t trailer_checksum)
241 {
242   char total_bytes_written_buf[4];
243   ssize_t n;
244 
245   assert (ctx);
246   assert (ctx->magic == IPMI_SDR_CTX_MAGIC);
247   assert (ipmi_ctx);
248   assert (fd);
249 
250   /* + 4 for this value, + 1 for checksum at end */
251   total_bytes_written += 4;
252   total_bytes_written += 1;
253 
254   total_bytes_written_buf[0] = (total_bytes_written & 0x000000FF);
255   total_bytes_written_buf[1] = (total_bytes_written & 0x0000FF00) >> 8;
256   total_bytes_written_buf[2] = (total_bytes_written & 0x00FF0000) >> 16;
257   total_bytes_written_buf[3] = (total_bytes_written & 0xFF000000) >> 24;
258 
259   if ((n = fd_write_n (fd, total_bytes_written_buf, 4)) < 0)
260     {
261       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
262       return (-1);
263     }
264   if (n != 4)
265     {
266       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
267       return (-1);
268     }
269 
270   trailer_checksum = ipmi_checksum_final (total_bytes_written_buf, 4, trailer_checksum);
271 
272   if ((n = fd_write_n (fd, (char *)&trailer_checksum, 1)) < 0)
273     {
274       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
275       return (-1);
276     }
277   if (n != 1)
278     {
279       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
280       return (-1);
281     }
282 
283   return (0);
284 }
285 
286 static int
_sdr_cache_reservation_id(ipmi_sdr_ctx_t ctx,ipmi_ctx_t ipmi_ctx,uint16_t * reservation_id)287 _sdr_cache_reservation_id (ipmi_sdr_ctx_t ctx,
288                            ipmi_ctx_t ipmi_ctx,
289                            uint16_t *reservation_id)
290 {
291   fiid_obj_t obj_cmd_rs = NULL;
292   uint64_t val;
293   int rv = -1;
294 
295   assert (ctx);
296   assert (ctx->magic == IPMI_SDR_CTX_MAGIC);
297   assert (ipmi_ctx);
298   assert (reservation_id);
299 
300   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_reserve_sdr_repository_rs)))
301     {
302       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
303       goto cleanup;
304     }
305 
306   if (ipmi_cmd_reserve_sdr_repository (ipmi_ctx, obj_cmd_rs) < 0)
307     {
308       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_IPMI_ERROR);
309       goto cleanup;
310     }
311 
312   *reservation_id = 0;
313   if (FIID_OBJ_GET (obj_cmd_rs,
314                     "reservation_id",
315                     &val) < 0)
316     {
317       SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
318       goto cleanup;
319     }
320   *reservation_id = val;
321 
322   rv = 0;
323  cleanup:
324   fiid_obj_destroy (obj_cmd_rs);
325   return (rv);
326 }
327 
328 static int
_sdr_cache_get_record(ipmi_sdr_ctx_t ctx,ipmi_ctx_t ipmi_ctx,uint16_t record_id,void * record_buf,unsigned int record_buf_len,uint16_t * reservation_id,uint16_t * next_record_id)329 _sdr_cache_get_record (ipmi_sdr_ctx_t ctx,
330                        ipmi_ctx_t ipmi_ctx,
331                        uint16_t record_id,
332                        void *record_buf,
333                        unsigned int record_buf_len,
334                        uint16_t *reservation_id,
335                        uint16_t *next_record_id)
336 {
337   fiid_obj_t obj_cmd_rs = NULL;
338   fiid_obj_t obj_sdr_record_header = NULL;
339   int sdr_record_header_length = 0;
340   int sdr_record_len = 0;
341   unsigned int record_length = 0;
342   int rv = -1;
343   unsigned int bytes_to_read = IPMI_SDR_CACHE_BYTES_TO_READ_START;
344   unsigned int offset_into_record = 0;
345   unsigned int reservation_id_retry_count = 0;
346   uint8_t temp_record_buf[IPMI_SDR_MAX_RECORD_LENGTH];
347   uint64_t val;
348 
349   assert (ctx);
350   assert (ctx->magic == IPMI_SDR_CTX_MAGIC);
351   assert (ipmi_ctx);
352   assert (record_buf);
353   assert (record_buf_len);
354   assert (reservation_id);
355   assert (next_record_id);
356 
357   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_get_sdr_rs)))
358     {
359       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
360       goto cleanup;
361     }
362 
363   if (!(obj_sdr_record_header = fiid_obj_create (tmpl_sdr_record_header)))
364     {
365       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
366       goto cleanup;
367     }
368 
369   if ((sdr_record_header_length = fiid_template_len_bytes (tmpl_sdr_record_header)) < 0)
370     {
371       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
372       goto cleanup;
373     }
374 
375   /* achu:
376    *
377    * Many motherboards now allow you to read the full SDR record, try
378    * that first.  If it fails for any reason, bail and try to read via
379    * partial reads.
380    */
381 
382   reservation_id_retry_count = 0;
383   while (!offset_into_record)
384     {
385       if (ipmi_cmd_get_sdr (ipmi_ctx,
386                             *reservation_id,
387                             record_id,
388                             0,
389                             IPMI_SDR_READ_ENTIRE_RECORD_BYTES_TO_READ,
390                             obj_cmd_rs) < 0)
391         {
392           if (ipmi_ctx_errnum (ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE)
393             {
394               uint8_t comp_code;
395 
396               if (FIID_OBJ_GET (obj_cmd_rs,
397                                 "comp_code",
398                                 &val) < 0)
399                 {
400                   SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
401                   goto cleanup;
402                 }
403               comp_code = val;
404 
405               if (comp_code == IPMI_COMP_CODE_RESERVATION_CANCELLED
406                   && (reservation_id_retry_count < IPMI_SDR_CACHE_MAX_RESERVATION_ID_RETRY))
407                 {
408                   if (_sdr_cache_reservation_id (ctx,
409                                                  ipmi_ctx,
410                                                  reservation_id) < 0)
411                     goto cleanup;
412                   reservation_id_retry_count++;
413                   continue;
414                 }
415             }
416 
417           goto partial_read;
418         }
419 
420       if ((sdr_record_len = fiid_obj_get_data (obj_cmd_rs,
421                                                "record_data",
422                                                temp_record_buf,
423                                                IPMI_SDR_MAX_RECORD_LENGTH)) < 0)
424         {
425           SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
426           goto cleanup;
427         }
428 
429       /* Assume this is an "IPMI Error", fall through to partial reads */
430       if (sdr_record_len < sdr_record_header_length)
431         goto partial_read;
432 
433       /*
434        * IPMI Workaround (achu)
435        *
436        * Discovered on Xyratex HB-F8-SRAY
437        *
438        * For some reason reading the entire SDR record (with
439        * IPMI_SDR_READ_ENTIRE_RECORD_BYTES_TO_READ) the response
440        * returns fewer bytes than the actual length of the record.
441        * However, when reading with partial reads things ultimately
442        * succeed.  If we notice the length is off, we fall out and do
443        * a partial read.
444        */
445       if ((((uint8_t)temp_record_buf[IPMI_SDR_RECORD_LENGTH_INDEX]) + IPMI_SDR_RECORD_HEADER_LENGTH) > sdr_record_len)
446         goto partial_read;
447 
448       if (sdr_record_len > record_buf_len)
449         {
450           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
451           goto cleanup;
452         }
453 
454       if (FIID_OBJ_GET (obj_cmd_rs,
455                         "next_record_id",
456                         &val) < 0)
457         {
458           SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
459           goto cleanup;
460         }
461       *next_record_id = val;
462 
463       memcpy (record_buf, temp_record_buf, sdr_record_len);
464       offset_into_record += sdr_record_len;
465       goto out;
466     }
467 
468  partial_read:
469 
470   reservation_id_retry_count = 0;
471   while (!record_length)
472     {
473       uint8_t record_header_buf[IPMI_SDR_MAX_RECORD_LENGTH];
474       int sdr_record_header_len;
475 
476       if (ipmi_cmd_get_sdr (ipmi_ctx,
477                             *reservation_id,
478                             record_id,
479                             0,
480                             sdr_record_header_length,
481                             obj_cmd_rs) < 0)
482         {
483           if (ipmi_ctx_errnum (ipmi_ctx) != IPMI_ERR_BAD_COMPLETION_CODE)
484             {
485               SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_IPMI_ERROR);
486               goto cleanup;
487             }
488           else
489             {
490               uint8_t comp_code;
491 
492               if (FIID_OBJ_GET (obj_cmd_rs,
493                                 "comp_code",
494                                 &val) < 0)
495                 {
496                   SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
497                   goto cleanup;
498                 }
499               comp_code = val;
500 
501               if (comp_code == IPMI_COMP_CODE_RESERVATION_CANCELLED
502                   && (reservation_id_retry_count < IPMI_SDR_CACHE_MAX_RESERVATION_ID_RETRY))
503                 {
504                   if (_sdr_cache_reservation_id (ctx,
505                                                  ipmi_ctx,
506                                                  reservation_id) < 0)
507                     goto cleanup;
508                   reservation_id_retry_count++;
509                   continue;
510                 }
511 
512               SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_IPMI_ERROR);
513               goto cleanup;
514             }
515         }
516 
517       if ((sdr_record_header_len = fiid_obj_get_data (obj_cmd_rs,
518                                                       "record_data",
519                                                       record_header_buf,
520                                                       IPMI_SDR_MAX_RECORD_LENGTH)) < 0)
521         {
522           SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
523           goto cleanup;
524         }
525 
526       if (sdr_record_header_len < sdr_record_header_length)
527         {
528           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_IPMI_ERROR);
529           goto cleanup;
530         }
531 
532       if (fiid_obj_set_all (obj_sdr_record_header,
533                             record_header_buf,
534                             sdr_record_header_len) < 0)
535         {
536           SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_sdr_record_header);
537           goto cleanup;
538         }
539 
540       if (FIID_OBJ_GET (obj_sdr_record_header,
541                         "record_length",
542                         &val) < 0)
543         {
544           SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_sdr_record_header);
545           goto cleanup;
546         }
547 
548       if (sdr_record_header_len > record_buf_len)
549         {
550           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
551           goto cleanup;
552         }
553 
554       /* copy header into buf */
555       memcpy (record_buf, record_header_buf, sdr_record_header_len);
556       offset_into_record += sdr_record_header_len;
557       record_length = val + sdr_record_header_length;
558     }
559 
560   if (record_length > record_buf_len)
561     {
562       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
563       goto cleanup;
564     }
565 
566   if (FIID_OBJ_GET (obj_cmd_rs,
567                     "next_record_id",
568                     &val) < 0)
569     {
570       SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
571       goto cleanup;
572     }
573   *next_record_id = val;
574 
575   reservation_id_retry_count = 0;
576   while (offset_into_record < record_length)
577     {
578       int record_data_len;
579 
580       if ((record_length - offset_into_record) < bytes_to_read)
581         bytes_to_read = record_length - offset_into_record;
582 
583       if (ipmi_cmd_get_sdr (ipmi_ctx,
584                             *reservation_id,
585                             record_id,
586                             offset_into_record,
587                             bytes_to_read,
588                             obj_cmd_rs) < 0)
589         {
590           /* Workaround
591            *
592            * Dell Poweredge FC830
593            *
594            * Last SDR record can't be read, it always returns
595            * 0xC3.  If this is the last record, just don't return
596            * a record back to the caller.
597            */
598           if (ipmi_ctx_errnum (ipmi_ctx) == IPMI_ERR_MESSAGE_TIMEOUT)
599             {
600               uint8_t comp_code;
601 
602               if (FIID_OBJ_GET (obj_cmd_rs,
603                                 "comp_code",
604                                 &val) < 0)
605                 {
606                   SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
607                   goto cleanup;
608                 }
609               comp_code = val;
610 
611               if (comp_code == IPMI_COMP_CODE_COMMAND_TIMEOUT
612                   && (*next_record_id) == IPMI_SDR_RECORD_ID_LAST)
613                 {
614                   offset_into_record = 0;
615                   goto out;
616                 }
617             }
618 
619           if (ipmi_ctx_errnum (ipmi_ctx) != IPMI_ERR_BAD_COMPLETION_CODE)
620             {
621               SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_IPMI_ERROR);
622               goto cleanup;
623             }
624           else
625             {
626               uint8_t comp_code;
627 
628               if (FIID_OBJ_GET (obj_cmd_rs,
629                                 "comp_code",
630                                 &val) < 0)
631                 {
632                   SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
633                   goto cleanup;
634                 }
635               comp_code = val;
636 
637               if (comp_code == IPMI_COMP_CODE_RESERVATION_CANCELLED
638                   && (reservation_id_retry_count < IPMI_SDR_CACHE_MAX_RESERVATION_ID_RETRY))
639                 {
640                   if (_sdr_cache_reservation_id (ctx,
641                                                  ipmi_ctx,
642                                                  reservation_id) < 0)
643                     goto cleanup;
644                   reservation_id_retry_count++;
645                   continue;
646                 }
647               else if  ((comp_code == IPMI_COMP_CODE_CANNOT_RETURN_REQUESTED_NUMBER_OF_BYTES
648                          || comp_code == IPMI_COMP_CODE_UNSPECIFIED_ERROR)
649                         && bytes_to_read > sdr_record_header_length)
650                 {
651                   bytes_to_read -= IPMI_SDR_CACHE_BYTES_TO_READ_DECREMENT;
652                   if (bytes_to_read < sdr_record_header_length)
653                     bytes_to_read = sdr_record_header_length;
654                   continue;
655                 }
656 
657               SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_IPMI_ERROR);
658               goto cleanup;
659             }
660         }
661 
662       if ((record_data_len = fiid_obj_get_data (obj_cmd_rs,
663                                                 "record_data",
664                                                 record_buf + offset_into_record,
665                                                 record_buf_len - offset_into_record)) < 0)
666         {
667           SDR_FIID_OBJECT_ERROR_TO_SDR_ERRNUM (ctx, obj_cmd_rs);
668           goto cleanup;
669         }
670 
671       offset_into_record += record_data_len;
672     }
673 
674  out:
675   rv = offset_into_record;
676  cleanup:
677   fiid_obj_destroy (obj_cmd_rs);
678   fiid_obj_destroy (obj_sdr_record_header);
679   return (rv);
680 }
681 
682 static int
_sdr_cache_record_write(ipmi_sdr_ctx_t ctx,int fd,unsigned int * total_bytes_written,uint16_t * record_ids,unsigned int * record_ids_count,uint8_t * buf,unsigned int buflen,uint8_t * trailer_checksum)683 _sdr_cache_record_write (ipmi_sdr_ctx_t ctx,
684                          int fd,
685                          unsigned int *total_bytes_written,
686                          uint16_t *record_ids,
687                          unsigned int *record_ids_count,
688                          uint8_t *buf,
689                          unsigned int buflen,
690                          uint8_t *trailer_checksum)
691 {
692   ssize_t n;
693 
694   assert (ctx);
695   assert (ctx->magic == IPMI_SDR_CTX_MAGIC);
696   assert (fd);
697   assert (total_bytes_written);
698   assert (!record_ids || (record_ids && record_ids_count));
699   assert (buf);
700   assert (buflen);
701   assert (trailer_checksum);
702 
703   /* Record header bytes are 5 bytes */
704   if (buflen < IPMI_SDR_RECORD_HEADER_LENGTH)
705     {
706       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_INVALID_RECORD_LENGTH);
707       return (-1);
708     }
709 
710   /* Record Length plus the header bytes should match buflen. */
711   if ((((uint8_t)buf[IPMI_SDR_RECORD_LENGTH_INDEX]) + IPMI_SDR_RECORD_HEADER_LENGTH) != buflen)
712     {
713       /*
714        * IPMI Workaround (achu)
715        *
716        * Discovered on HP Proliant DL585G7
717        *
718        * When reading an entire SDR record (using
719        * IPMI_SDR_READ_ENTIRE_RECORD_BYTES_TO_READ), sometimes records
720        * are returned with an excess of bytes.  The following
721        * truncates the buffer length to the correct size.
722        */
723       if ((((uint8_t)buf[IPMI_SDR_RECORD_LENGTH_INDEX]) + IPMI_SDR_RECORD_HEADER_LENGTH) <= buflen)
724         buflen = ((uint8_t)buf[IPMI_SDR_RECORD_LENGTH_INDEX]) + IPMI_SDR_RECORD_HEADER_LENGTH;
725       else
726         {
727           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_INVALID_RECORD_LENGTH);
728           return (-1);
729         }
730     }
731 
732   if (record_ids)
733     {
734       uint16_t record_id;
735       unsigned int i;
736 
737       /* Record ID stored little endian */
738       record_id = ((uint16_t)buf[IPMI_SDR_RECORD_ID_INDEX_LS] & 0xFF);
739       record_id |= ((uint16_t)buf[IPMI_SDR_RECORD_ID_INDEX_MS] & 0xFF) << 8;
740 
741       for (i = 0; i < *record_ids_count; i++)
742         {
743           if (record_ids[i] == record_id)
744             {
745               SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_DUPLICATE_RECORD_ID);
746               return (-1);
747             }
748         }
749       record_ids[*record_ids_count] = record_id;
750       (*record_ids_count)++;
751     }
752 
753   if ((n = fd_write_n (fd, buf, buflen)) < 0)
754     {
755       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
756       return (-1);
757     }
758 
759   if (n != buflen)
760     {
761       /* Try to lseek back to our original spot */
762       lseek (fd, SEEK_SET, *total_bytes_written);
763       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
764       return (-1);
765     }
766   (*total_bytes_written) += buflen;
767 
768   (*trailer_checksum) = ipmi_checksum_incremental (buf, buflen, (*trailer_checksum));
769 
770   return (0);
771 
772 }
773 
774 int
ipmi_sdr_cache_create(ipmi_sdr_ctx_t ctx,ipmi_ctx_t ipmi_ctx,const char * filename,int cache_create_flags,Ipmi_Sdr_Cache_Create_Callback create_callback,void * create_callback_data)775 ipmi_sdr_cache_create (ipmi_sdr_ctx_t ctx,
776                        ipmi_ctx_t ipmi_ctx,
777                        const char *filename,
778                        int cache_create_flags,
779                        Ipmi_Sdr_Cache_Create_Callback create_callback,
780                        void *create_callback_data)
781 {
782   int open_flags;
783   uint8_t sdr_version;
784   uint16_t record_count, reservation_id, record_id, next_record_id;
785   uint32_t most_recent_addition_timestamp, most_recent_erase_timestamp;
786   unsigned int record_count_written = 0;
787   unsigned int total_bytes_written = 0;
788   uint16_t *record_ids = NULL;
789   unsigned int record_ids_count = 0;
790   unsigned int cache_create_flags_mask = (IPMI_SDR_CACHE_CREATE_FLAGS_OVERWRITE
791                                           | IPMI_SDR_CACHE_CREATE_FLAGS_DUPLICATE_RECORD_ID
792                                           | IPMI_SDR_CACHE_CREATE_FLAGS_ASSUME_MAX_SDR_RECORD_COUNT);
793   uint8_t trailer_checksum = 0;
794   int fd = -1;
795   int rv = -1;
796 
797   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
798     {
799       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
800       return (-1);
801     }
802 
803   /* Version cannot be 0h according to the IPMI spec */
804   if (!ipmi_ctx
805       || !filename
806       || (strlen (filename) > MAXPATHLEN)
807       || (cache_create_flags & ~cache_create_flags_mask))
808     {
809       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
810       return (-1);
811     }
812 
813   if (ctx->operation != IPMI_SDR_OPERATION_UNINITIALIZED)
814     {
815       if (ctx->operation == IPMI_SDR_OPERATION_READ_CACHE)
816         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CONTEXT_PERFORMING_OTHER_OPERATION);
817       else
818         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
819       return (-1);
820     }
821 
822   ctx->operation = IPMI_SDR_OPERATION_CREATE_CACHE;
823 
824   if (cache_create_flags & IPMI_SDR_CACHE_CREATE_FLAGS_OVERWRITE)
825     open_flags = O_CREAT | O_TRUNC | O_WRONLY;
826   else
827     open_flags = O_CREAT | O_EXCL | O_WRONLY;
828 
829   if ((fd = open (filename, open_flags, 0644)) < 0)
830     {
831       if (!(cache_create_flags & IPMI_SDR_CACHE_CREATE_FLAGS_OVERWRITE)
832           && errno == EEXIST)
833         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_CACHE_EXISTS);
834       else if (errno == EPERM
835                || errno == EACCES
836                || errno == EISDIR
837                || errno == EROFS)
838         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PERMISSION);
839       else if (errno == ENAMETOOLONG
840                || errno == ENOENT
841                || errno == ELOOP)
842         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_FILENAME_INVALID);
843       else if (errno == ENOSPC
844                || errno == EMFILE
845                || errno == ENFILE)
846         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_FILESYSTEM);
847       else
848         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
849       goto cleanup;
850     }
851 
852   if (sdr_info (ctx,
853                 ipmi_ctx,
854                 &sdr_version,
855                 &record_count,
856                 &most_recent_addition_timestamp,
857                 &most_recent_erase_timestamp) < 0)
858     goto cleanup;
859 
860   if (!record_count)
861     {
862       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_INVALID_RECORD_COUNT);
863       goto cleanup;
864     }
865 
866   if (_sdr_cache_header_write (ctx,
867                                ipmi_ctx,
868                                fd,
869                                &total_bytes_written,
870                                sdr_version,
871                                record_count,
872                                most_recent_addition_timestamp,
873                                most_recent_erase_timestamp) < 0)
874     goto cleanup;
875 
876   /* Version cannot be 0h according to the IPMI spec, but we accept it regardless */
877   ctx->sdr_version = sdr_version;
878   ctx->record_count = record_count;
879   ctx->most_recent_addition_timestamp = most_recent_addition_timestamp;
880   ctx->most_recent_erase_timestamp = most_recent_erase_timestamp;
881 
882   if (cache_create_flags & IPMI_SDR_CACHE_CREATE_FLAGS_DUPLICATE_RECORD_ID)
883     {
884       if (!(record_ids = (uint16_t *)malloc (ctx->record_count * sizeof (uint16_t))))
885         {
886           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_OUT_OF_MEMORY);
887           goto cleanup;
888         }
889       record_ids_count = 0;
890     }
891 
892   if (_sdr_cache_reservation_id (ctx,
893                                  ipmi_ctx,
894                                  &reservation_id) < 0)
895     goto cleanup;
896 
897   next_record_id = IPMI_SDR_RECORD_ID_FIRST;
898   while (next_record_id != IPMI_SDR_RECORD_ID_LAST)
899     {
900       uint8_t record_buf[IPMI_SDR_MAX_RECORD_LENGTH];
901       int record_len;
902 
903       if (record_count_written >= ctx->record_count)
904         {
905           /* IPMI Workaround
906            *
907            * Discovered on unspecified Inspur motherboard
908            *
909            * SDR record reading is broken, the IPMI_SDR_RECORD_ID_LAST
910            * record id never occurs.  So this workaround allows the
911            * user to not error out and avoids this loop from looping
912            * infinitely.
913            *
914            */
915 
916           if (cache_create_flags & IPMI_SDR_CACHE_CREATE_FLAGS_ASSUME_MAX_SDR_RECORD_COUNT)
917             break;
918 
919           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_INVALID_RECORD_COUNT);
920           goto cleanup;
921         }
922 
923       record_id = next_record_id;
924       if ((record_len = _sdr_cache_get_record (ctx,
925                                                ipmi_ctx,
926                                                record_id,
927                                                record_buf,
928                                                IPMI_SDR_MAX_RECORD_LENGTH,
929                                                &reservation_id,
930                                                &next_record_id)) < 0)
931         goto cleanup;
932 
933       if (record_len)
934         {
935           if (ctx->flags & IPMI_SDR_FLAGS_DEBUG_DUMP)
936             {
937               const char *record_str;
938 
939               if ((record_str = sdr_record_type_str (ctx,
940                                                      record_buf,
941                                                      record_len)))
942                 {
943                   char hdrbuf[IPMI_SDR_CACHE_DEBUG_BUFLEN + 1];
944 
945                   memset (hdrbuf, '\0', IPMI_SDR_CACHE_DEBUG_BUFLEN + 1);
946 
947                   debug_hdr_str (DEBUG_UTIL_TYPE_NONE,
948                                  DEBUG_UTIL_DIRECTION_NONE,
949                                  DEBUG_UTIL_FLAGS_DEFAULT,
950                                  record_str,
951                                  hdrbuf,
952                                  IPMI_SDR_CACHE_DEBUG_BUFLEN);
953 
954                   ipmi_dump_sdr_record (STDERR_FILENO,
955                                         ctx->debug_prefix,
956                                         hdrbuf,
957                                         NULL,
958                                         record_buf,
959                                         record_len);
960                 }
961             }
962 
963           if (_sdr_cache_record_write (ctx,
964                                        fd,
965                                        &total_bytes_written,
966                                        record_ids,
967                                        &record_ids_count,
968                                        record_buf,
969                                        record_len,
970                                        &trailer_checksum) < 0)
971             goto cleanup;
972 
973           record_count_written++;
974 
975           if (create_callback)
976             (*create_callback)(ctx->sdr_version,
977                                ctx->record_count,
978                                ctx->most_recent_addition_timestamp,
979                                ctx->most_recent_erase_timestamp,
980                                record_id,
981                                create_callback_data);
982         }
983     }
984 
985   if (record_count_written != ctx->record_count)
986     {
987       /*
988        * IPMI Workaround (achu)
989        *
990        * Discovered on Fujitsu RX 100
991        * Discovered on Fujitsu RX300/200-S8
992        *
993        * The record_count listed from the Get SDR Repository Info command
994        * is not consistent with the length of SDR records stored.
995        *
996        * We will assume that if we reached the end of the SDR record
997        * list (i.e. next_record_id == 0xFFFF), a non-zero number of
998        * records were written, it's ok and we can continue on.
999        */
1000       /* Note Dell Poweredge FC830 workaround above, this code is used
1001        * as a consequence of that workaround
1002        */
1003       if (next_record_id == IPMI_SDR_RECORD_ID_LAST
1004           && record_count_written)
1005         {
1006           unsigned int total_bytes_written_temp = 0;
1007 
1008           ctx->record_count = record_count_written;
1009 
1010           /* need to seek back to the beginning of the file and
1011            * re-write the header info with the correct number of
1012            * records
1013            */
1014 
1015           if (lseek (fd, 0, SEEK_SET) < 0)
1016             {
1017               SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
1018               goto cleanup;
1019             }
1020 
1021           if (_sdr_cache_header_write (ctx,
1022                                        ipmi_ctx,
1023                                        fd,
1024                                        &total_bytes_written_temp,
1025                                        ctx->sdr_version,
1026                                        ctx->record_count,
1027                                        ctx->most_recent_addition_timestamp,
1028                                        ctx->most_recent_erase_timestamp) < 0)
1029             goto cleanup;
1030 
1031           /* need to seek back to the end of the file to write the
1032            * trailer below
1033            */
1034           if (lseek (fd, 0, SEEK_END) < 0)
1035             {
1036               SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
1037               goto cleanup;
1038             }
1039         }
1040       else
1041         {
1042           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_INVALID_RECORD_COUNT);
1043           goto cleanup;
1044         }
1045     }
1046 
1047   if (_sdr_cache_trailer_write (ctx,
1048                                 ipmi_ctx,
1049                                 fd,
1050                                 total_bytes_written,
1051                                 trailer_checksum) < 0)
1052             goto cleanup;
1053 
1054   if (fsync (fd) < 0)
1055     {
1056       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
1057       goto cleanup;
1058     }
1059 
1060   if (close (fd) < 0)
1061     {
1062       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
1063       goto cleanup;
1064     }
1065   fd = -1;
1066 
1067   rv = 0;
1068   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
1069  cleanup:
1070   ctx->operation = IPMI_SDR_OPERATION_UNINITIALIZED;
1071   if (fd >= 0)
1072     {
1073       /* If the cache create never completed, try to remove the file */
1074       /* ignore potential error, cleanup path */
1075       unlink (filename);
1076       /* ignore potential error, cleanup path */
1077       close (fd);
1078     }
1079   free (record_ids);
1080   sdr_init_ctx (ctx);
1081   return (rv);
1082 }
1083