freeipmi-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Freeipmi-devel] Get IPMI device info code - 1st cut, RFC


From: Ian Zimmerman
Subject: [Freeipmi-devel] Get IPMI device info code - 1st cut, RFC
Date: 11 Dec 2003 16:05:01 -0800
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3

/* This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#if HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

/* SMBIOS Reference Specification: map area between 000f0000 and
   000fffff.  The IPMI Entry Structure begins on a 16-byte boundary,
   with a 4 byte "_SM_" signature.  */

#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <limits.h>

enum ipmi_interface
{
  ipmi_interface_kcs = 1,
  ipmi_interface_smic = 2,
  ipmi_interface_bt = 3,
  ipmi_interface_last
};

typedef enum ipmi_interface ipmi_interface_t;

enum ipmi_reg_spacing
{
  ipmi_reg_spacing_1 = 0,
  ipmi_reg_spacing_2 = 1,
  ipmi_reg_spacing_4 = 2,
  ipmi_reg_spacing_last
};

typedef enum ipmi_reg_spacing ipmi_reg_spacing_t;

struct ipmi_device_info
{
  ipmi_interface_t type;

  struct
  {
    int8_t minor;
    int8_t major;
  }
  version;

  u_int8_t i2c_slave_address;
  u_int8_t nvstor_device_address;
  int bmc_io_mapped;

  union
  {
    u_int64_t bmc_iobase_address;
    u_int64_t bmc_membase_address;
  }
  base;

  ipmi_reg_spacing_t reg_spacing;

  struct
  {
    int intinfo_specified;
    int intinfo_polarity_high;
    int intinfo_trigger_level;
  }
  intr;

  u_int8_t intr_num;
};

#define ipmi_minor version.minor
#define ipmi_major version.major
#define ipmi_membase base.bmc_membase_address
#define ipmi_iobase base.bmc_iobase_address
#define ipmi_int_specified intr.intinfo_specified
#define ipmi_int_polarity intr.intinfo_polarity_high
#define ipmi_int_trigger intr.intinfo_trigger_level

typedef struct ipmi_device_info ipmi_device_info_t;

/* is_ipmi_entry */

/* arguments: */
/* sigp = points to start of purported SMBIOS entry structure */

/* returns: */
/* 0 = not really a SMBIOS entry structure */
/* 1 = yes, a real SMBIOS entry structure */

#define SMBIOS_ENTRY_CSUM_OFFSET        0x4
#define SMBIOS_ENTRY_LEN_OFFSET         0x5
#define SMBIOS_ENTRY_ANCHOR_OFFSET      0x10
#define SMBIOS_ENTRY_ANCHOR_CSUM_OFFSET 0x15

static int
is_ipmi_entry (u_int8_t* sigp)
{
  static const char smbios_entry_sig[4] = { '_', 'S', 'M', '_' };
  static const char smbios_entry_anchor[5] = { '_', 'D', 'M', 'I', '_' };
  u_int32_t csum;
  u_int8_t entry_len;
  u_int8_t* bp;

  if (memcmp (sigp, smbios_entry_sig, sizeof (smbios_entry_sig)) != 0)
    return 0;

  csum = sigp[SMBIOS_ENTRY_CSUM_OFFSET];
  entry_len = sigp[SMBIOS_ENTRY_LEN_OFFSET];
  for (bp = sigp; bp < sigp + entry_len; bp++)
    csum = (csum + (*bp)) % (1 << CHAR_BIT);
  if (csum != 0)
    return 0;

  if (memcmp (sigp + SMBIOS_ENTRY_ANCHOR_OFFSET, smbios_entry_anchor,
              sizeof (smbios_entry_anchor)) != 0)
    return 0;

  csum = sigp[SMBIOS_ENTRY_ANCHOR_CSUM_OFFSET];
  for (bp = sigp + SMBIOS_ENTRY_ANCHOR_CSUM_OFFSET; bp < sigp + entry_len; bp++)
    csum = (csum + (*bp)) % (1 << CHAR_BIT);
  if (csum != 0)
    return 0;

  return 1;

}

/* is_ipmi_dev_info */

/* arguments: */
/* type = which interface (KCS, SMIC, BT) */
/* dev_info_p = points to start of purported IPMI device info structure */

/* returns: */
/* 0 = not a IPMI device info structure for TYPE */
/* 1 = yes, IPMI device info structure for TYPE */

#define SMBIOS_IPMI_DEV_INFO_SIG                38
#define SMBIOS_IPMI_DEV_INFO_TYPE_OFFSET        0x4

static int
is_ipmi_dev_info (ipmi_interface_t type, u_int8_t* dev_info_p)
{
  if (*dev_info_p != SMBIOS_IPMI_DEV_INFO_SIG)
    return 0;

  if (dev_info_p[SMBIOS_IPMI_DEV_INFO_TYPE_OFFSET] != type)
    return 0;

  return 1;
}

/* copy_impi_device_info */

/* arguments: */
/* type = which interface (KCS, SMIC, BT) */
/* statusp = optional (NULL allowed) pointer to store status
   information: 1 - structure not found, -1 - error, 0 - success */

/* returns: */
/* pointer to the device info structure in heap (caller responsible
   for freeing */

#define SMBIOS_AREA_START               0x000f0000
#define SMBIOS_AREA_END                 0x000fffff
#define SMBIOS_AREA_LEN                 ((SMBIOS_AREA_END - SMBIOS_AREA_START) 
+ 1)
#define SMBIOS_AREA_ALIGN               16
#define SMBIOS_ENTRY_TLEN_OFFSET        0x16
#define SMBIOS_ENTRY_PTR_OFFSET         0x18
#define SMBIOS_DEV_INFO_LEN_OFFSET      0x1

static u_int8_t*
copy_impi_device_info (ipmi_interface_t type, int* statusp)
{
  int mem_fd;
  int status;
  u_int8_t* result;

  status = 1;
  mem_fd = open ("/dev/mem", O_RDONLY);
  if (mem_fd != -1)
    {
      u_int8_t* pmem; 

      pmem = mmap (NULL, SMBIOS_AREA_LEN, PROT_READ, MAP_PRIVATE,
                   mem_fd, SMBIOS_AREA_START);
      if (pmem != NULL)
        {
          u_int8_t* sigp;

          for (sigp = pmem; sigp - pmem < SMBIOS_AREA_LEN; sigp += 
SMBIOS_AREA_ALIGN)
            {
              if (is_ipmi_entry (sigp))
                {
                  u_int16_t s_table_len;
                  u_int8_t* s_table_p;
                  u_int8_t* dev_info_p;
                  size_t size;

                  s_table_len = *(u_int16_t*)(sigp + SMBIOS_ENTRY_TLEN_OFFSET);
                  s_table_p = (u_int8_t*)(*(u_int32_t*)(sigp + 
SMBIOS_ENTRY_PTR_OFFSET));
                  dev_info_p = s_table_p;
                  size = dev_info_p[SMBIOS_DEV_INFO_LEN_OFFSET];
                  while (dev_info_p - s_table_p < s_table_len)
                    {
                      if (is_ipmi_dev_info (type, dev_info_p))
                        {
                          result = malloc (size);
                          if (result != NULL)
                            status = 0;
                          else
                            {
                              errno = ENOMEM;
                              status = -1;
                            }
                          break;
                        }
                      dev_info_p += size;
                      size = dev_info_p[SMBIOS_DEV_INFO_LEN_OFFSET];
                    }
                }
              if (status <= 0)
                break;
            }
          munmap (pmem, SMBIOS_AREA_LEN);
        }
      close (mem_fd);
    }
  if (statusp != NULL)
    *statusp = status;
  return (status == 0 ? result : NULL);
}

/* get_ipmi_device_info */

/* arguments: */
/* type = which interface (KCS, SMIC, BT) */
/* pinfo = pointer to information structure filled in by this function */
/* statusp = optional (NULL allowed) pointer to store status
   information: 1 - structure not found, -1 - error, 0 - success */

/* returns: */
/* pinfo if successful, NULL otherwise */

#define SMBIOS_IPMI_DEV_INFO_VER_OFFSET         0x5
#define SMBIOS_IPMI_DEV_INFO_I2C_OFFSET         0x6
#define SMBIOS_IPMI_DEV_INFO_NVSTOR_OFFSET      0x7
#define SMBIOS_IPMI_DEV_INFO_ADDRESS_OFFSET     0x8
#define SMBIOS_IPMI_DEV_INFO_MODIFIER_OFFSET    0x10
#define SMBIOS_LSB_BIT                          4
#define SMBIOS_REGSPACING_SHIFT                 6
#define SMBIOS_REGSPACING_MASK                  0x3
#define SMBIOS_INTINFO_PRESENT_BIT              3
#define SMBIOS_INTINFO_POLARITY_BIT             1
#define SMBIOS_INTINFO_TRIGGER_BIT              0
#define SMBIOS_DEV_INFO_INTNUM_OFFSET           0x11

ipmi_device_info_t*
get_ipmi_device_info (ipmi_interface_t type, ipmi_device_info_t* pinfo, int* 
statusp)
{
  u_int8_t* bufp;
  u_int8_t version;
  u_int64_t address;
  u_int8_t modifier;
  u_int8_t lsb;

  bufp = copy_impi_device_info (type, statusp);
  if (bufp == NULL)
    return NULL;

  pinfo->type = bufp[SMBIOS_IPMI_DEV_INFO_TYPE_OFFSET];
  version = bufp[SMBIOS_IPMI_DEV_INFO_VER_OFFSET];
  pinfo->ipmi_major = (version >> 4) & 0xf;
  pinfo->ipmi_minor = version & 0xf;
  pinfo->i2c_slave_address = bufp[SMBIOS_IPMI_DEV_INFO_I2C_OFFSET];
  pinfo->nvstor_device_address = bufp[SMBIOS_IPMI_DEV_INFO_NVSTOR_OFFSET];

  address = bufp[SMBIOS_IPMI_DEV_INFO_ADDRESS_OFFSET];
  modifier = bufp[SMBIOS_IPMI_DEV_INFO_MODIFIER_OFFSET];
  lsb = (modifier >> SMBIOS_LSB_BIT) & 1;
  if ((address & 1) != 0)
    {
      pinfo->bmc_io_mapped = 0;
      pinfo->ipmi_iobase = address | lsb;
    }
  else
    {
      pinfo->bmc_io_mapped = 1;
      pinfo->ipmi_membase = address | lsb;
    }

  pinfo->reg_spacing = (modifier >> SMBIOS_REGSPACING_SHIFT) & 
SMBIOS_REGSPACING_MASK;

  if ((modifier >> SMBIOS_INTINFO_PRESENT_BIT) & 1 != 0)
    {
      pinfo->ipmi_int_specified = 1;
      pinfo->ipmi_int_polarity = (modifier >> SMBIOS_INTINFO_POLARITY_BIT) & 1;
      pinfo->ipmi_int_trigger = (modifier >> SMBIOS_INTINFO_TRIGGER_BIT) & 1;
    }
  else
    pinfo->ipmi_int_specified = 0;

  pinfo->intr_num = bufp[SMBIOS_DEV_INFO_INTNUM_OFFSET];

  free (bufp);
  return pinfo;
}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]