xref: /openbmc/ipmitool/lib/ipmi_gendev.c (revision c18ec02f)
1 /*
2  * Copyright (c) 2003 Kontron Canada, Inc.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * Redistribution of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * Redistribution in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * Neither the name of Sun Microsystems, Inc. or the names of
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * This software is provided "AS IS," without a warranty of any kind.
20  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
23  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
26  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
27  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
28  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
29  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  */
32 
33 #include <string.h>
34 
35 #include <math.h>
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <time.h>
40 
41 #include <ipmitool/ipmi.h>
42 #include <ipmitool/log.h>
43 #include <ipmitool/ipmi_mc.h>
44 #include <ipmitool/ipmi_sdr.h>
45 #include <ipmitool/ipmi_gendev.h>
46 #include <ipmitool/ipmi_intf.h>
47 #include <ipmitool/ipmi_sel.h>
48 #include <ipmitool/ipmi_entity.h>
49 #include <ipmitool/ipmi_constants.h>
50 #include <ipmitool/ipmi_strings.h>
51 #include <ipmitool/ipmi_raw.h>
52 
53 #if HAVE_CONFIG_H
54 # include <config.h>
55 #endif
56 
57 extern int verbose;
58 
59 
60 #define GENDEV_RETRY_COUNT    5
61 #define GENDEV_MAX_SIZE       16
62 
63 typedef struct gendev_eeprom_info
64 {
65    uint32_t size;
66    uint16_t page_size;
67    uint8_t  address_span;
68    uint8_t  address_length;
69 }t_gendev_eeprom_info;
70 
71 
72 static int
ipmi_gendev_get_eeprom_size(struct ipmi_intf * intf,struct sdr_record_generic_locator * dev,t_gendev_eeprom_info * info)73 ipmi_gendev_get_eeprom_size(
74                         struct ipmi_intf *intf,
75                         struct sdr_record_generic_locator *dev,
76                         t_gendev_eeprom_info *info
77                      )
78 {
79    int eeprom_size = 0;
80    /*
81    lprintf(LOG_ERR, "Gen Device : %s", dev->id_string);
82    lprintf(LOG_ERR, "Access Addr: %x", dev->dev_access_addr);
83    lprintf(LOG_ERR, "Slave Addr : %x", dev->dev_slave_addr);
84    lprintf(LOG_ERR, "Channel Num: %x", dev->channel_num);
85    lprintf(LOG_ERR, "Lun        : %x", dev->lun);
86    lprintf(LOG_ERR, "Bus        : %x", dev->bus);
87    lprintf(LOG_ERR, "Addr Span  : %x", dev->addr_span);
88    lprintf(LOG_ERR, "DevType    : %x", dev->dev_type);
89    lprintf(LOG_ERR, "DevType Mod: %x", dev->dev_type_modifier);
90    */
91    if( info != NULL)
92    {
93       switch(dev->dev_type)
94       {
95          case 0x08:  // 24C01
96             info->size = 128;
97             info->page_size = 8;
98             info->address_span = dev->addr_span;
99             info->address_length = 1;
100          break;
101          case 0x09:  // 24C02
102             info->size = 256;
103             info->page_size = 8;
104             info->address_span = dev->addr_span;
105             info->address_length = 1;
106          break;
107          case 0x0A:  // 24C04
108             info->size = 512;
109             info->page_size = 8;
110             info->address_span = dev->addr_span;
111             info->address_length = 2;
112          break;
113          case 0x0B:  // 24C08
114             info->size = 1024;
115             info->page_size = 8;
116             info->address_span = dev->addr_span;
117             info->address_length = 2;
118          break;
119          case 0x0C:  // 24C16
120             info->size = 2048;
121             info->page_size = 256;
122             info->address_span = dev->addr_span;
123             info->address_length = 2;
124          break;
125          case 0x0D:  // 24C17
126             info->size = 2048;
127             info->page_size = 256;
128             info->address_span = dev->addr_span;
129             info->address_length = 2;
130          break;
131          case 0x0E:  // 24C32
132             info->size = 4096;
133             info->page_size = 8;
134             info->address_span = dev->addr_span;
135             info->address_length = 2;
136          break;
137          case 0x0F:  // 24C64
138             info->size = 8192;
139             info->page_size = 32;
140             info->address_span = dev->addr_span;
141             info->address_length = 2;
142          break;
143          case 0xC0:  // Proposed OEM Code for 24C128
144             info->size = 16384;
145             info->page_size = 64;
146             info->address_span = dev->addr_span;
147             info->address_length = 2;
148          break;
149          case 0xC1:  // Proposed OEM Code for 24C256
150             info->size = 32748;
151             info->page_size = 64;
152             info->address_span = dev->addr_span;
153             info->address_length = 2;
154          break;
155          case 0xC2:  // Proposed OEM Code for 24C512
156             info->size = 65536;
157             info->page_size = 128;
158             info->address_span = dev->addr_span;
159             info->address_length = 2;
160          break;
161          case 0xC3:  // Proposed OEM Code for 24C1024
162             info->size = 131072;
163             info->page_size = 128;
164             info->address_span = dev->addr_span;
165             info->address_length = 2;
166          break;
167          /* Please reserved up to CFh for future update */
168          default:   // Not a eeprom, return size = 0;
169             info->size = 0;
170             info->page_size = 0;
171             info->address_span = 0;
172             info->address_length = 0;
173          break;
174       }
175 
176       eeprom_size = info->size;
177    }
178 
179    return eeprom_size;
180 }
181 
182 
183 
184 static int
ipmi_gendev_read_file(struct ipmi_intf * intf,struct sdr_record_generic_locator * dev,const char * ofile)185 ipmi_gendev_read_file(
186                         struct ipmi_intf *intf,
187                         struct sdr_record_generic_locator *dev,
188                         const char *ofile
189                      )
190 {
191    int rc = 0;
192    int eeprom_size;
193    t_gendev_eeprom_info eeprom_info;
194 
195    eeprom_size = ipmi_gendev_get_eeprom_size(intf, dev, &eeprom_info);
196 
197    if(eeprom_size > 0)
198    {
199       FILE *fp;
200 
201       /* now write to file */
202       fp = ipmi_open_file_write(ofile);
203 
204       if(fp)
205       {
206          struct ipmi_rs *rsp;
207          int numWrite;
208          uint32_t counter;
209          uint8_t msize;
210          uint8_t channel = dev->channel_num;
211          uint8_t i2cbus = dev->bus;
212          uint8_t i2caddr = dev->dev_slave_addr;
213          uint8_t privatebus = 1;
214          uint32_t address_span_size;
215          uint8_t percentCompleted = 0;
216 
217 
218          /* Handle Address Span */
219          if( eeprom_info.address_span != 0)
220          {
221             address_span_size =
222                (eeprom_info.size / (eeprom_info.address_span+1));
223          }
224          else
225          {
226             address_span_size = eeprom_info.size;
227          }
228 
229          /* Setup read/write size */
230          if( eeprom_info.page_size < GENDEV_MAX_SIZE)
231          {
232             msize = eeprom_info.page_size;
233          }
234          else
235          {
236             msize = GENDEV_MAX_SIZE;
237                // All eeprom with page higher than 32 is on the
238                // 16 bytes boundary
239          }
240 
241          /* Setup i2c bus byte */
242          i2cbus = ((channel & 0xF) << 4) | ((i2cbus & 7) << 1) | privatebus;
243 
244 /*
245          lprintf(LOG_ERR, "Generic device: %s", dev->id_string);
246          lprintf(LOG_ERR, "I2C Chnl: %x", channel);
247          lprintf(LOG_ERR, "I2C Bus : %x", i2cbus);
248          lprintf(LOG_ERR, "I2C Addr: %x", i2caddr);    */
249 
250          for (
251                counter = 0;
252                (counter < (eeprom_info.size)) && (rc == 0);
253                counter+= msize
254              )
255          {
256             uint8_t retryCounter;
257 
258             for(
259                   retryCounter = 0;
260                   retryCounter<GENDEV_RETRY_COUNT;
261                   retryCounter ++
262                )
263             {
264                uint8_t wrByte[GENDEV_MAX_SIZE+2];
265 
266                wrByte[0] =  (uint8_t) (counter>>0);
267                if(eeprom_info.address_length > 1)
268                {
269                   wrByte[1] =  (uint8_t) (counter>>8);
270                }
271 
272                i2caddr+= (((eeprom_info.size) % address_span_size) * 2);
273 
274                rsp = ipmi_master_write_read(
275                            intf,
276                            i2cbus,
277                            i2caddr,
278                            (uint8_t *) wrByte,
279                            eeprom_info.address_length,
280                            msize
281                            );
282 
283                if (rsp != NULL)
284                {
285                   retryCounter = GENDEV_RETRY_COUNT;
286                   rc = 0;
287                }
288                else if(retryCounter < GENDEV_RETRY_COUNT)
289                {
290                   retryCounter ++;
291                   lprintf(LOG_ERR, "Retry");
292                   sleep(1);
293                   rc = -1;
294                }
295                else
296                {
297                   lprintf(LOG_ERR, "Unable to perform I2C Master Write-Read");
298                   rc = -1;
299                }
300             }
301 
302             if( rc == 0 )
303             {
304                static uint8_t previousCompleted = 101;
305                numWrite = fwrite(rsp->data, 1, msize, fp);
306                if (numWrite != msize)
307                {
308                   lprintf(LOG_ERR, "Error writing file %s", ofile);
309                   rc = -1;
310                   break;
311                }
312 
313                percentCompleted = ((counter * 100) / eeprom_info.size );
314 
315                if(percentCompleted != previousCompleted)
316                {
317                   printf("\r%i percent completed", percentCompleted);
318                   previousCompleted = percentCompleted;
319                }
320 
321 
322             }
323          }
324          if(counter == (eeprom_info.size))
325          {
326             printf("\r%%100 percent completed\n");
327          }
328          else
329          {
330             printf("\rError: %i percent completed, read not completed \n", percentCompleted);
331          }
332 
333          fclose(fp);
334       }
335    }
336    else
337    {
338       lprintf(LOG_ERR, "The selected generic device is not an eeprom");
339    }
340 
341    return rc;
342 }
343 
344 
345 /* ipmi_gendev_write_file  -  Read raw SDR from binary file
346  *
347  * used for writing generic locator device Eeprom type
348  *
349  * @intf:	ipmi interface
350  * @dev:		generic device to read
351  * @ofile:	output filename
352  *
353  * returns 0 on success
354  * returns -1 on error
355  */
356 static int
ipmi_gendev_write_file(struct ipmi_intf * intf,struct sdr_record_generic_locator * dev,const char * ofile)357 ipmi_gendev_write_file(
358                         struct ipmi_intf *intf,
359                         struct sdr_record_generic_locator *dev,
360                         const char *ofile
361                      )
362 {
363    int rc = 0;
364    int eeprom_size;
365    t_gendev_eeprom_info eeprom_info;
366 
367    eeprom_size = ipmi_gendev_get_eeprom_size(intf, dev, &eeprom_info);
368 
369    if(eeprom_size > 0)
370    {
371       FILE *fp;
372       uint32_t fileLength = 0;
373 
374       /* now write to file */
375       fp = ipmi_open_file_read(ofile);
376 
377       if(fp)
378       {
379          /* Retreive file length, check if it's fits the Eeprom Size */
380          fseek(fp, 0 ,SEEK_END);
381          fileLength = ftell(fp);
382 
383          lprintf(LOG_ERR, "File   Size: %i", fileLength);
384          lprintf(LOG_ERR, "Eeprom Size: %i", eeprom_size);
385          if(fileLength != eeprom_size)
386          {
387             lprintf(LOG_ERR, "File size does not fit Eeprom Size");
388             fclose(fp);
389             fp = NULL;
390          }
391          else
392          {
393             fseek(fp, 0 ,SEEK_SET);
394          }
395       }
396 
397       if(fp)
398       {
399          struct ipmi_rs *rsp;
400          int numRead;
401          uint32_t counter;
402          uint8_t msize;
403          uint8_t channel = dev->channel_num;
404          uint8_t i2cbus = dev->bus;
405          uint8_t i2caddr = dev->dev_slave_addr;
406          uint8_t privatebus = 1;
407          uint32_t address_span_size;
408          uint8_t percentCompleted = 0;
409 
410 
411          /* Handle Address Span */
412          if( eeprom_info.address_span != 0)
413          {
414             address_span_size =
415                (eeprom_info.size / (eeprom_info.address_span+1));
416          }
417          else
418          {
419             address_span_size = eeprom_info.size;
420          }
421 
422          /* Setup read/write size */
423          if( eeprom_info.page_size < GENDEV_MAX_SIZE)
424          {
425             msize = eeprom_info.page_size;
426          }
427          else
428          {
429             msize = GENDEV_MAX_SIZE;
430                      // All eeprom with page higher than 32 is on the
431                      // 16 bytes boundary
432          }
433 
434          /* Setup i2c bus byte */
435          i2cbus = ((channel & 0xF) << 4) | ((i2cbus & 7) << 1) | privatebus;
436 
437 /*
438          lprintf(LOG_ERR, "Generic device: %s", dev->id_string);
439          lprintf(LOG_ERR, "I2C Chnl: %x", channel);
440          lprintf(LOG_ERR, "I2C Bus : %x", i2cbus);
441          lprintf(LOG_ERR, "I2C Addr: %x", i2caddr);    */
442 
443          for (
444                counter = 0;
445                (counter < (eeprom_info.size)) && (rc == 0);
446                counter+= msize
447              )
448          {
449             uint8_t retryCounter;
450             uint8_t readByte[GENDEV_MAX_SIZE];
451 
452             numRead = fread(readByte, 1, msize, fp);
453             if (numRead != msize)
454             {
455                lprintf(LOG_ERR, "Error reading file %s", ofile);
456                rc = -1;
457                break;
458             }
459 
460 
461 
462             for(
463                   retryCounter = 0;
464                   retryCounter<GENDEV_RETRY_COUNT;
465                   retryCounter ++
466                )
467             {
468                uint8_t wrByte[GENDEV_MAX_SIZE+2];
469                wrByte[0] =  (uint8_t) (counter>>0);
470                if(eeprom_info.address_length > 1)
471                {
472                   wrByte[1] =  (uint8_t) (counter>>8);
473                }
474                memcpy(&wrByte[eeprom_info.address_length], readByte, msize);
475 
476                i2caddr+= (((eeprom_info.size) % address_span_size) * 2);
477 
478                rsp = ipmi_master_write_read(intf, i2cbus, i2caddr, (uint8_t *) wrByte, eeprom_info.address_length+msize, 0);
479                if (rsp != NULL)
480                {
481                   retryCounter = GENDEV_RETRY_COUNT;
482                   rc = 0;
483                }
484                else if(retryCounter < GENDEV_RETRY_COUNT)
485                {
486                   retryCounter ++;
487                   lprintf(LOG_ERR, "Retry");
488                   sleep(1);
489                   rc = -1;
490                }
491                else
492                {
493                   lprintf(LOG_ERR, "Unable to perform I2C Master Write-Read");
494                   rc = -1;
495                }
496             }
497 
498             if( rc == 0 )
499             {
500                static uint8_t previousCompleted = 101;
501                percentCompleted = ((counter * 100) / eeprom_info.size );
502 
503                if(percentCompleted != previousCompleted)
504                {
505                   printf("\r%i percent completed", percentCompleted);
506                   previousCompleted = percentCompleted;
507                }
508 
509             }
510          }
511          if(counter == (eeprom_info.size))
512          {
513             printf("\r%%100 percent completed\n");
514          }
515          else
516          {
517             printf("\rError: %i percent completed, read not completed \n", percentCompleted);
518          }
519 
520          fclose(fp);
521       }
522    }
523    else
524    {
525       lprintf(LOG_ERR, "The selected generic device is not an eeprom");
526    }
527 
528    return rc;
529 }
530 
531 
532 /* ipmi_gendev_main  -  top-level handler for generic device
533  *
534  * @intf:	ipmi interface
535  * @argc:	number of arguments
536  * @argv:	argument list
537  *
538  * returns 0 on success
539  * returns -1 on error
540  */
541 int
ipmi_gendev_main(struct ipmi_intf * intf,int argc,char ** argv)542 ipmi_gendev_main(struct ipmi_intf *intf, int argc, char **argv)
543 {
544    int rc = 0;
545 
546    /* initialize random numbers used later */
547    srand(time(NULL));
548 
549    lprintf(LOG_ERR, "Rx gendev command: %s", argv[0]);
550 
551    if (
552          (argc == 0)
553          ||
554          (strncmp(argv[0], "help", 4) == 0)
555       )
556    {
557       lprintf(LOG_ERR,
558          "SDR Commands:  list read write");
559       lprintf(LOG_ERR,
560          "                     list                     List All Generic Device Locators");
561       lprintf(LOG_ERR,
562          "                     read <sdr name> <file>   Read to file eeprom specify by Generic Device Locators");
563       lprintf(LOG_ERR,
564          "                     write <sdr name> <file>  Write from file eeprom specify by Generic Device Locators");
565    }
566    else if ( strncmp(argv[0], "list", 4) == 0)
567    {
568       rc = ipmi_sdr_print_sdr(intf,
569                   SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR);
570    }
571    else if (strncmp(argv[0], "read", 4) == 0)
572    {
573       if (argc < 3)
574          lprintf(LOG_ERR, "usage: gendev read <gendev> <filename>");
575       else
576       {
577          struct sdr_record_list *sdr;
578 
579          lprintf(LOG_ERR, "Gendev read sdr name : %s", argv[1]);
580 
581          printf("Locating sensor record '%s'...\n", argv[1]);
582 
583          /* lookup by sensor name */
584          sdr = ipmi_sdr_find_sdr_byid(intf, argv[1]);
585          if (sdr == NULL)
586          {
587             lprintf(LOG_ERR, "Sensor data record not found!");
588             return -1;
589          }
590 
591          if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR)
592          {
593             lprintf(LOG_ERR, "Target SDR is not a generic device locator");
594             return -1;
595          }
596 
597          lprintf(LOG_ERR, "Gendev read file name: %s", argv[2]);
598          ipmi_gendev_read_file(intf, sdr->record.genloc, argv[2]);
599 
600       }
601    }
602    else if (strncmp(argv[0], "write", 5) == 0)
603    {
604       if (argc < 3)
605          lprintf(LOG_ERR, "usage: gendev write <gendev> <filename>");
606       else
607       {
608          struct sdr_record_list *sdr;
609 
610          lprintf(LOG_ERR, "Gendev write sdr name : %s", argv[1]);
611 
612          printf("Locating sensor record '%s'...\n", argv[1]);
613 
614          /* lookup by sensor name */
615          sdr = ipmi_sdr_find_sdr_byid(intf, argv[1]);
616          if (sdr == NULL)
617          {
618             lprintf(LOG_ERR, "Sensor data record not found!");
619             return -1;
620          }
621 
622          if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR)
623          {
624             lprintf(LOG_ERR, "Target SDR is not a generic device locator");
625             return -1;
626          }
627 
628          lprintf(LOG_ERR, "Gendev write file name: %s", argv[2]);
629          ipmi_gendev_write_file(intf, sdr->record.genloc, argv[2]);
630 
631       }
632    }
633    else
634    {
635       lprintf(LOG_ERR, "Invalid gendev command: %s", argv[0]);
636       rc = -1;
637    }
638 
639    return rc;
640 }
641