xref: /openbmc/ipmitool/lib/ipmi_sdradd.c (revision 531569ec)
1 /*
2  * Redistribution and use in source and binary forms, with or without
3  * modification, are permitted provided that the following conditions
4  * are met:
5  *
6  * Redistribution of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  *
9  * Redistribution in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  *
13  * Neither the name of Sun Microsystems, Inc. or the names of
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * This software is provided "AS IS," without a warranty of any kind.
18  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
19  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
20  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
21  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
22  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
23  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
24  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
25  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
26  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
27  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
28  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
29  */
30 
31 /*
32  * Functions to program the SDR repository, from built-in sensors or
33  * from sensors dumped in a binary file.
34  */
35 
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <time.h>
40 #include <fcntl.h>
41 
42 #include <ipmitool/helper.h>
43 #include <ipmitool/log.h>
44 #include <ipmitool/bswap.h>
45 #include <ipmitool/ipmi.h>
46 #include <ipmitool/ipmi_intf.h>
47 #include <ipmitool/ipmi_mc.h>
48 #include <ipmitool/ipmi_strings.h>
49 
50 #include <ipmitool/ipmi_sdr.h>
51 
52 
53 #define ADD_PARTIAL_SDR 0x25
54 
55 #ifdef HAVE_PRAGMA_PACK
56 #pragma pack(1)
57 #endif
58 struct sdr_add_rq {
59   uint16_t reserve_id;  /* reservation ID */
60   uint16_t id;          /* record ID */
61   uint8_t offset;       /* offset into SDR */
62   uint8_t in_progress;  /* 0=partial, 1=last */
63 #define PARTIAL_ADD (0)
64 #define LAST_RECORD (1)
65   uint8_t data[1];      /* SDR record data */
66 } ATTRIBUTE_PACKING;
67 #ifdef HAVE_PRAGMA_PACK
68 #pragma pack(0)
69 #endif
70 
71 /* This was formerly initialized to 24, reduced this to 19 so the overall
72    message fits into the recommended 32-byte limit */
73 static int sdr_max_write_len = 19;
74 int ipmi_parse_range_list(const char *rangeList, unsigned char *pHexList);
75 int ipmi_hex_to_dec( char * rangeList, unsigned char * pDecValue);
76 
77 static int
78 partial_send(struct ipmi_intf *intf, struct ipmi_rq *req, uint16_t *id)
79 {
80   struct ipmi_rs *rsp;
81   rsp = intf->sendrecv(intf, req);
82   if (rsp == NULL) {
83     return -1;
84   }
85 
86   if (rsp->ccode || rsp->data_len < 2) {
87     return -1;
88   }
89 
90   *id = rsp->data[0] + (rsp->data[1] << 8);
91   return 0;
92 }
93 
94 int
95 ipmi_sdr_add_record(struct ipmi_intf *intf, struct sdr_record_list *sdrr)
96 {
97   struct ipmi_rq req;
98   struct sdr_add_rq *sdr_rq;
99   uint16_t reserve_id;
100   uint16_t id;
101   int i;
102   int len = sdrr->length;
103   int rc = 0;
104 
105   /* actually no SDR to program */
106   if (len < 1 || !sdrr->raw) {
107     lprintf(LOG_ERR, "ipmitool: bad record , skipped");
108     return 0;
109   }
110 
111   if (ipmi_sdr_get_reservation(intf, 0, &reserve_id)) {
112     lprintf(LOG_ERR, "ipmitool: reservation failed");
113     return -1;
114   }
115 
116   sdr_rq = (struct sdr_add_rq *)malloc(sizeof(*sdr_rq) + sdr_max_write_len);
117   if (sdr_rq == NULL) {
118     lprintf(LOG_ERR, "ipmitool: malloc failure");
119     return -1;
120   }
121   sdr_rq->reserve_id = reserve_id;
122   sdr_rq->in_progress = PARTIAL_ADD;
123 
124   memset(&req, 0, sizeof(req));
125   req.msg.netfn = IPMI_NETFN_STORAGE;
126   req.msg.cmd = ADD_PARTIAL_SDR;
127   req.msg.data = (uint8_t *) sdr_rq;
128 
129   /* header first */
130   sdr_rq->id = 0;
131   sdr_rq->offset = 0;
132   sdr_rq->data[0] = sdrr->id & 0xFF;
133   sdr_rq->data[1] = (sdrr->id >> 8) & 0xFF;
134   sdr_rq->data[2] = sdrr->version;
135   sdr_rq->data[3] = sdrr->type;
136   sdr_rq->data[4] = sdrr->length;
137   req.msg.data_len = 5 + sizeof(*sdr_rq) - 1;
138 
139   if (partial_send(intf, &req, &id)) {
140      lprintf(LOG_ERR, "ipmitool: partial send error");
141     free(sdr_rq);
142     sdr_rq = NULL;
143     return -1;
144   }
145 
146   i = 0;
147 
148   /* sdr entry */
149   while (i < len) {
150      int data_len = 0;
151      if ( (len - i) <= sdr_max_write_len) {
152       /* last crunch */
153       data_len = len - i;
154       sdr_rq->in_progress = LAST_RECORD;
155     } else {
156       data_len = sdr_max_write_len;
157     }
158 
159     sdr_rq->id = id;
160     sdr_rq->offset = i + 5;
161     memcpy(sdr_rq->data, sdrr->raw + i, data_len);
162     req.msg.data_len = data_len + sizeof(*sdr_rq) - 1;
163 
164     if ((rc = partial_send(intf, &req, &id)) != 0) {
165        lprintf(LOG_ERR, "ipmitool: partial add failed");
166       break;
167     }
168 
169     i += data_len;
170   }
171 
172   free(sdr_rq);
173   sdr_rq = NULL;
174   return rc;
175 }
176 
177 static int
178 ipmi_sdr_repo_clear(struct ipmi_intf *intf)
179 {
180   struct ipmi_rs * rsp;
181   struct ipmi_rq req;
182   uint8_t msg_data[8];
183   uint16_t reserve_id;
184   int try;
185 
186   if (ipmi_sdr_get_reservation(intf, 0, &reserve_id))
187     return -1;
188 
189   memset(&req, 0, sizeof(req));
190   req.msg.netfn = IPMI_NETFN_STORAGE;
191   req.msg.cmd = 0x27; // FIXME
192   req.msg.data = msg_data;
193   req.msg.data_len = 6;
194 
195   msg_data[0] = reserve_id & 0xFF;
196   msg_data[1] = reserve_id >> 8;
197   msg_data[2] = 'C';
198   msg_data[3] = 'L';
199   msg_data[4] = 'R';
200   msg_data[5] = 0xAA;
201 
202   for (try = 0; try < 5; try++) {
203     rsp = intf->sendrecv(intf, &req);
204     if (rsp == NULL) {
205       lprintf(LOG_ERR, "Unable to clear SDRR");
206       return -1;
207     }
208     if (rsp->ccode > 0) {
209       lprintf(LOG_ERR, "Unable to clear SDRR: %s",
210         val2str(rsp->ccode, completion_code_vals));
211       return -1;
212     }
213     if ((rsp->data[0] & 1) == 1) {
214       printf("SDRR successfully erased\n");
215       return 0;
216     }
217     printf("Wait for SDRR erasure completed...\n");
218     msg_data[5] = 0;
219     sleep(1);
220   }
221 
222   /* if we are here we fed up trying erase */
223   return -1;
224 }
225 
226 
227 struct sdrr_queue {
228   struct sdr_record_list *head;
229   struct sdr_record_list *tail;
230 };
231 
232 
233 /*
234  * Fill the SDR repository from built-in sensors
235  *
236  */
237 
238 /*
239  * Get all the SDR records stored in <queue>
240  */
241 static int
242 sdrr_get_records(struct ipmi_intf *intf, struct ipmi_sdr_iterator *itr,
243                  struct sdrr_queue *queue)
244 {
245   struct sdr_get_rs *header;
246 
247   queue->head = NULL;
248   queue->tail = NULL;
249 
250   while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL) {
251     struct sdr_record_list *sdrr;
252 
253     sdrr = malloc(sizeof (struct sdr_record_list));
254     if (sdrr == NULL) {
255       lprintf(LOG_ERR, "ipmitool: malloc failure");
256       return -1;
257     }
258     memset(sdrr, 0, sizeof (struct sdr_record_list));
259 
260     sdrr->id = header->id;
261     sdrr->version = header->version;
262     sdrr->type = header->type;
263     sdrr->length = header->length;
264     sdrr->raw = ipmi_sdr_get_record(intf, header, itr);
265     (void)ipmi_sdr_print_name_from_rawentry(intf,  sdrr->id, sdrr->type,sdrr->raw);
266 
267     /* put in the record queue */
268     if (queue->head == NULL)
269       queue->head = sdrr;
270     else
271       queue->tail->next = sdrr;
272     queue->tail = sdrr;
273   }
274   return 0;
275 }
276 
277 static int
278 sdr_copy_to_sdrr(struct ipmi_intf *intf, int use_builtin,
279                  int from_addr, int to_addr)
280 {
281   int rc;
282   struct sdrr_queue sdrr_queue;
283   struct ipmi_sdr_iterator *itr;
284   struct sdr_record_list *sdrr;
285   struct sdr_record_list *sdrr_next;
286 
287   /* generate list of records for this target */
288   intf->target_addr = from_addr;
289 
290   /* initialize iterator */
291   itr = ipmi_sdr_start(intf, use_builtin);
292   if (itr == 0)
293     return 0;
294 
295   printf("Load SDRs from 0x%x\n", from_addr);
296   rc = sdrr_get_records(intf, itr, &sdrr_queue);
297   ipmi_sdr_end(intf, itr);
298   /* ... */
299 
300   /* write the SDRs to the destination SDR Repository */
301   intf->target_addr = to_addr;
302   for (sdrr = sdrr_queue.head; sdrr != NULL; sdrr = sdrr_next) {
303     sdrr_next = sdrr->next;
304     rc = ipmi_sdr_add_record(intf, sdrr);
305     if(rc < 0){
306       lprintf(LOG_ERR, "Cannot add SDR ID 0x%04x to repository...", sdrr->id);
307     }
308     free(sdrr);
309     sdrr = NULL;
310   }
311   return rc;
312 }
313 
314 int
315 ipmi_sdr_add_from_sensors(struct ipmi_intf *intf, int maxslot)
316 {
317   int i;
318   int rc = 0;
319   int slave_addr;
320   int myaddr = intf->target_addr;
321 
322   if (ipmi_sdr_repo_clear(intf)) {
323     lprintf(LOG_ERR, "Cannot erase SDRR. Give up.");
324     return -1;
325   }
326 
327   /* First fill the SDRR from local built-in sensors */
328   rc = sdr_copy_to_sdrr(intf, 1, myaddr, myaddr);
329 
330   /* Now fill the SDRR with remote sensors */
331   if( maxslot != 0 ) {
332      for (i = 0, slave_addr = 0xB0; i < maxslot; i++, slave_addr += 2) {
333         /* Hole in the PICMG 2.9 mapping */
334         if (slave_addr == 0xC2) slave_addr += 2;
335         if(sdr_copy_to_sdrr(intf, 0, slave_addr, myaddr) < 0)
336         {
337            rc = -1;
338         }
339      }
340   }
341   return rc;
342 }
343 
344 int ipmi_hex_to_dec( char * strchar, unsigned char * pDecValue)
345 {
346   int rc = -1;
347   unsigned char retValue = 0;
348 
349   if(
350      (strlen(strchar) == 4)
351      &&
352      (strchar[0] == '0')
353      &&
354      (strchar[1] == 'x')
355     )
356   {
357       rc = 0;
358 
359       if((strchar[2] >= '0') && (strchar[2] <= '9'))
360       {
361         retValue += ((strchar[2]-'0') * 16);
362       }
363       else if((strchar[2] >= 'a') && (strchar[2] <= 'f'))
364       {
365         retValue += (((strchar[2]-'a') + 10) * 16);
366       }
367       else if((strchar[2] >= 'A') && (strchar[2] <= 'F'))
368       {
369         retValue += (((strchar[2]-'A') + 10) * 16);
370       }
371       else
372       {
373         rc = -1;
374       }
375 
376       if((strchar[3] >= '0') && (strchar[3] <= '9'))
377       {
378         retValue += ((strchar[3]-'0'));
379       }
380       else if((strchar[3] >= 'a') && (strchar[3] <= 'f'))
381       {
382         retValue += (((strchar[3]-'a') + 10));
383       }
384       else if((strchar[3] >= 'A') && (strchar[3] <= 'F'))
385       {
386         retValue += (((strchar[3]-'A') + 10));
387       }
388       else
389       {
390         rc = -1;
391       }
392   }
393 
394   if(rc == 0)
395   {
396     * pDecValue = retValue;
397   }
398   else
399   {
400     lprintf(LOG_ERR, "Must be Hex value of 4 characters (Ex.: 0x24)");
401   }
402 
403   return rc;
404 }
405 
406 
407 
408 #define MAX_NUM_SLOT  128
409 int ipmi_parse_range_list(const char *rangeList, unsigned char * pHexList)
410 {
411   int rc = -1;
412 
413   unsigned char listOffset = 0;
414   char * nextString;
415   char * rangeString;
416   char * inProcessString = (char *) rangeList;
417 
418   /* Discard empty string */
419   if(strlen(rangeList) == 0)
420   {
421     return rc;
422   }
423 
424   /* First, cut to comma separated string */
425   nextString = strstr( rangeList, "," );
426 
427   if(nextString != rangeList)
428   {
429     unsigned char isLast;
430     /* We get a valid string so far */
431     rc = 0;
432 
433     do
434     {
435       if(nextString != NULL)
436       {
437         (*nextString)= 0;
438         nextString   ++;
439         isLast = 0;
440       }
441       else
442       {
443         isLast = 1;
444       }
445 
446       /* At this point, it is a single entry or a range */
447       rangeString = strstr( inProcessString, "-" );
448       if(rangeString == NULL)
449       {
450         unsigned char decValue = 0;
451 
452         /* Single entry */
453         rc = ipmi_hex_to_dec( inProcessString, &decValue);
454 
455         if(rc == 0)
456         {
457           if((decValue % 2) == 0)
458           {
459             pHexList[listOffset++] = decValue;
460           }
461           else
462           {
463             lprintf(LOG_ERR, "I2C address provided value must be even.");
464           }
465         }
466       }
467       else
468       {
469         unsigned char startValue = 0;
470         unsigned char endValue = 0;
471 
472 
473         (*rangeString)= 0; /* Cut string*/
474         rangeString ++;
475 
476         /* Range */
477         rc = ipmi_hex_to_dec( inProcessString, &startValue);
478         if(rc == 0)
479           rc = ipmi_hex_to_dec( rangeString, &endValue);
480 
481         if(rc == 0)
482         {
483           if(((startValue % 2) == 0) && ((endValue % 2) == 0))
484           {
485             do
486             {
487               pHexList[listOffset++] = startValue;
488               startValue += 2;
489             }
490             while(startValue != endValue);
491             pHexList[listOffset++] = endValue;
492           }
493           else
494           {
495             lprintf(LOG_ERR, "I2C address provided value must be even.");
496           }
497         }
498       }
499 
500       if(isLast == 0)
501       {
502         /* Setup for next string */
503         inProcessString = nextString;
504         nextString = strstr( rangeList, "," );
505       }
506     }while ((isLast == 0) && (rc == 0));
507   }
508 
509   return rc;
510 }
511 
512 int
513 ipmi_sdr_add_from_list(struct ipmi_intf *intf, const char *rangeList)
514 {
515   int rc = 0;
516   int slave_addr;
517   int myaddr = intf->target_addr;
518   unsigned char listValue[MAX_NUM_SLOT];
519 
520   memset( listValue, 0, MAX_NUM_SLOT );
521 
522   /* Build list from string */
523   if(ipmi_parse_range_list(rangeList, listValue) != 0)
524   {
525     lprintf(LOG_ERR, "Range - List invalid, cannot be parsed.");
526     return -1;
527   }
528 
529   {
530     unsigned char counter = 0;
531     printf("List to scan: (Built-in) ");
532     while(listValue[counter] != 0)
533     {
534       printf("%02x ", listValue[counter]);
535       counter++;
536     }
537     printf("\n");
538   }
539 
540   printf("Clearing SDR Repository\n");
541   if (ipmi_sdr_repo_clear(intf)) {
542     lprintf(LOG_ERR, "Cannot erase SDRR. Give up.");
543     return -1;
544   }
545 
546   /* First fill the SDRR from local built-in sensors */
547   printf("Sanning built-in sensors..\n");
548   rc = sdr_copy_to_sdrr(intf, 1, myaddr, myaddr);
549 
550   /* Now fill the SDRR with provided sensors list */
551   {
552     unsigned char counter = 0;
553     while((rc == 0) && (listValue[counter] != 0))
554     {
555       slave_addr = listValue[counter];
556       printf("Scanning %02Xh..\n", slave_addr);
557       if(sdr_copy_to_sdrr(intf, 0, slave_addr, myaddr) < 0)
558       {
559          rc = -1;
560       }
561       counter++;
562     }
563   }
564 
565   return rc;
566 }
567 
568 
569 /*
570  * Fill the SDR repository from records stored in a binary file
571  *
572  */
573 
574 static int
575 ipmi_sdr_read_records(const char *filename, struct sdrr_queue *queue)
576 {
577   int rc = 0;
578   int fd;
579   uint8_t binHdr[5];
580 
581   queue->head = NULL;
582   queue->tail = NULL;
583 
584   if ((fd = open(filename, O_RDONLY)) < 0) {
585     return -1;
586   }
587 
588   while (read(fd, binHdr, 5) == 5) {
589 
590     struct sdr_record_list *sdrr;
591 
592     lprintf(LOG_DEBUG, "binHdr[0] (id[MSB]) = 0x%02x", binHdr[0]);
593     lprintf(LOG_DEBUG, "binHdr[1] (id[LSB]) = 0x%02x", binHdr[1]);
594     lprintf(LOG_DEBUG, "binHdr[2] (version) = 0x%02x", binHdr[2]);
595     lprintf(LOG_DEBUG, "binHdr[3] (type) = 0x%02x", binHdr[3]);
596     lprintf(LOG_DEBUG, "binHdr[4] (length) = 0x%02x", binHdr[4]);
597 
598     sdrr = malloc(sizeof(*sdrr));
599     if (sdrr == NULL) {
600       lprintf(LOG_ERR, "ipmitool: malloc failure");
601       rc = -1;
602       break;
603     }
604     sdrr->id = (binHdr[1] << 8) | binHdr[0];  // LS Byte first
605     sdrr->version = binHdr[2];
606     sdrr->type = binHdr[3];
607     sdrr->length = binHdr[4];
608 
609     if ((sdrr->raw = malloc(sdrr->length)) == NULL) {
610       lprintf(LOG_ERR, "ipmitool: malloc failure");
611       free(sdrr);
612       sdrr = NULL;
613       rc = -1;
614       break;
615     }
616 
617     if (read(fd, sdrr->raw, sdrr->length) != sdrr->length) {
618       lprintf(LOG_ERR, "SDR from '%s' truncated", filename);
619       free(sdrr->raw);
620       sdrr->raw = NULL;
621       free(sdrr);
622       sdrr = NULL;
623       rc = -1;
624       break;
625     }
626 
627     /* put in the record queue */
628     if (queue->head == NULL)
629       queue->head = sdrr;
630     else
631       queue->tail->next = sdrr;
632     queue->tail = sdrr;
633   }
634   close(fd);
635   return rc;
636 }
637 
638 int
639 ipmi_sdr_add_from_file(struct ipmi_intf *intf, const char *ifile)
640 {
641   int rc;
642   struct sdrr_queue sdrr_queue;
643   struct sdr_record_list *sdrr;
644   struct sdr_record_list *sdrr_next;
645 
646   /* read the SDR records from file */
647   rc = ipmi_sdr_read_records(ifile, &sdrr_queue);
648 
649   if (ipmi_sdr_repo_clear(intf)) {
650     lprintf(LOG_ERR, "Cannot erase SDRR. Giving up.");
651     /* FIXME: free sdr list */
652     return -1;
653   }
654 
655   /* write the SDRs to the SDR Repository */
656   for (sdrr = sdrr_queue.head; sdrr != NULL; sdrr = sdrr_next) {
657     sdrr_next = sdrr->next;
658     rc = ipmi_sdr_add_record(intf, sdrr);
659     if(rc < 0){
660       lprintf(LOG_ERR, "Cannot add SDR ID 0x%04x to repository...", sdrr->id);
661     }
662     free(sdrr);
663     sdrr = NULL;
664   }
665   return rc;
666 }
667 
668