diff mbox

[4/8] ipmi: add FRU support

Message ID 1452015002-28493-5-git-send-email-clg@fr.ibm.com
State New
Headers show

Commit Message

Cédric Le Goater Jan. 5, 2016, 5:29 p.m. UTC
This patch provides a simplistic FRU support for the IPMI BMC
simulator.  The FRU area contains 32 entries * 256 bytes which should
be enough to start some simulation.

Signed-off-by: Cédric Le Goater <clg@fr.ibm.com>
---
 hw/ipmi/ipmi_bmc_sim.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)

Comments

Corey Minyard Jan. 8, 2016, 7:41 p.m. UTC | #1
On 01/05/2016 11:29 AM, Cédric Le Goater wrote:
> This patch provides a simplistic FRU support for the IPMI BMC
> simulator.  The FRU area contains 32 entries * 256 bytes which should
> be enough to start some simulation.
>
> Signed-off-by: Cédric Le Goater <clg@fr.ibm.com>
> ---
>   hw/ipmi/ipmi_bmc_sim.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 119 insertions(+)
>
> diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c
> index 5db94491b130..60586a67104e 100644
> --- a/hw/ipmi/ipmi_bmc_sim.c
> +++ b/hw/ipmi/ipmi_bmc_sim.c
> @@ -81,6 +81,9 @@
>   #define IPMI_CMD_ENTER_SDR_REP_UPD_MODE   0x2A
>   #define IPMI_CMD_EXIT_SDR_REP_UPD_MODE    0x2B
>   #define IPMI_CMD_RUN_INIT_AGENT           0x2C
> +#define IPMI_CMD_GET_FRU_AREA_INFO        0x10
> +#define IPMI_CMD_READ_FRU_DATA            0x11
> +#define IPMI_CMD_WRITE_FRU_DATA           0x12
>   #define IPMI_CMD_GET_SEL_INFO             0x40
>   #define IPMI_CMD_GET_SEL_ALLOC_INFO       0x41
>   #define IPMI_CMD_RESERVE_SEL              0x42
> @@ -123,6 +126,14 @@ typedef struct IPMISdr {
>       uint8_t overflow;
>   } IPMISdr;
>   
> +/* theoretically, the offset being 16bits, it should be 65536 */
> +#define MAX_FRU_SIZE 256
> +#define MAX_FRU_ID 32
> +
> +typedef struct IPMIFru {
> +    uint8_t data[MAX_FRU_SIZE][MAX_FRU_ID];
> +} IPMIFru;

Instead of a static table like this, I think it would be better to make 
this configurable somehow.  I say this because I've never seen a system 
with 32 FRU devices on a BMC, but I've seen plenty with FRU data larger 
than 256 bytes.  By default, 1 FRU device with 2048 bytes is pretty 
reasonable, I think.

I'm not exactly sure the best way to make it configurable.  I assume 
that you need your platform code to be able to provide that information, 
and it could be passed in as BMC configuration parameters.  The ability 
to load the FRU data at startup is probably also necessary.

-corey

> +
>   typedef struct IPMISensor {
>       uint8_t status;
>       uint8_t reading;
> @@ -206,6 +217,7 @@ struct IPMIBmcSim {
>   
>       IPMISel sel;
>       IPMISdr sdr;
> +    IPMIFru fru;
>       IPMISensor sensors[MAX_SENSORS];
>   
>       /* Odd netfns are for responses, so we only need the even ones. */
> @@ -1305,6 +1317,110 @@ static void get_sel_info(IPMIBmcSim *ibs,
>       return;
>   }
>   
> +static void get_fru_area_info(IPMIBmcSim *ibs,
> +                         uint8_t *cmd, unsigned int cmd_len,
> +                         uint8_t *rsp, unsigned int *rsp_len,
> +                         unsigned int max_rsp_len)
> +{
> +    uint8_t fruid;
> +    uint16_t fru_entry_size;
> +
> +    IPMI_CHECK_CMD_LEN(3);
> +
> +    fruid = cmd[2];
> +
> +    if (fruid > MAX_FRU_ID) {
> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
> +        goto out;
> +    }
> +
> +    fru_entry_size = MAX_FRU_SIZE;
> +
> +    IPMI_ADD_RSP_DATA(fru_entry_size & 0xff);
> +    IPMI_ADD_RSP_DATA(fru_entry_size >> 8 & 0xff);
> +    IPMI_ADD_RSP_DATA(0x0);
> +out:
> +    return;
> +}
> +
> +#define min(x, y) ((x) < (y) ? (x) : (y))
> +#define max(x, y) ((x) > (y) ? (x) : (y))
> +
> +static void read_fru_data(IPMIBmcSim *ibs,
> +                         uint8_t *cmd, unsigned int cmd_len,
> +                         uint8_t *rsp, unsigned int *rsp_len,
> +                         unsigned int max_rsp_len)
> +{
> +    uint8_t fruid;
> +    uint16_t offset;
> +    int i;
> +    uint8_t *fru_entry;
> +    unsigned int count;
> +
> +    IPMI_CHECK_CMD_LEN(5);
> +
> +    fruid = cmd[2];
> +    offset = (cmd[3] | cmd[4] << 8);
> +
> +    if (fruid > MAX_FRU_ID) {
> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
> +        goto out;
> +    }
> +
> +    if (offset >= MAX_FRU_SIZE - 1) {
> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
> +        goto out;
> +    }
> +
> +    fru_entry = ibs->fru.data[fruid];
> +
> +    count = min(cmd[5], MAX_FRU_SIZE - offset);
> +
> +    IPMI_ADD_RSP_DATA(count & 0xff);
> +    for (i = 0; i < count; i++) {
> +        IPMI_ADD_RSP_DATA(fru_entry[offset + i]);
> +    }
> +
> + out:
> +    return;
> +}
> +
> +static void write_fru_data(IPMIBmcSim *ibs,
> +                         uint8_t *cmd, unsigned int cmd_len,
> +                         uint8_t *rsp, unsigned int *rsp_len,
> +                         unsigned int max_rsp_len)
> +{
> +    uint8_t fruid;
> +    uint16_t offset;
> +    uint8_t *fru_entry;
> +    unsigned int count;
> +
> +    IPMI_CHECK_CMD_LEN(5);
> +
> +    fruid = cmd[2];
> +    offset = (cmd[3] | cmd[4] << 8);
> +
> +    if (fruid > MAX_FRU_ID) {
> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
> +        goto out;
> +    }
> +
> +    if (offset >= MAX_FRU_SIZE - 1) {
> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
> +        goto out;
> +    }
> +
> +    fru_entry = ibs->fru.data[fruid];
> +
> +    count = min(cmd_len - 5, MAX_FRU_SIZE - offset);
> +
> +    memcpy(fru_entry + offset, cmd + 5, count);
> +
> +    IPMI_ADD_RSP_DATA(count & 0xff);
> + out:
> +    return;
> +}
> +
>   static void reserve_sel(IPMIBmcSim *ibs,
>                           uint8_t *cmd, unsigned int cmd_len,
>                           uint8_t *rsp, unsigned int *rsp_len,
> @@ -1682,6 +1798,9 @@ static const IPMINetfn app_netfn = {
>   };
>   
>   static const IPMICmdHandler storage_cmds[IPMI_NETFN_STORAGE_MAXCMD] = {
> +    [IPMI_CMD_GET_FRU_AREA_INFO] = get_fru_area_info,
> +    [IPMI_CMD_READ_FRU_DATA] = read_fru_data,
> +    [IPMI_CMD_WRITE_FRU_DATA] = write_fru_data,
>       [IPMI_CMD_GET_SDR_REP_INFO] = get_sdr_rep_info,
>       [IPMI_CMD_RESERVE_SDR_REP] = reserve_sdr_rep,
>       [IPMI_CMD_GET_SDR] = get_sdr,
Cédric Le Goater Jan. 12, 2016, 7:35 a.m. UTC | #2
Hello,

On 01/08/2016 08:41 PM, Corey Minyard wrote:
> On 01/05/2016 11:29 AM, Cédric Le Goater wrote:
>> This patch provides a simplistic FRU support for the IPMI BMC
>> simulator.  The FRU area contains 32 entries * 256 bytes which should
>> be enough to start some simulation.
>>
>> Signed-off-by: Cédric Le Goater <clg@fr.ibm.com>
>> ---
>>   hw/ipmi/ipmi_bmc_sim.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 119 insertions(+)
>>
>> diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c
>> index 5db94491b130..60586a67104e 100644
>> --- a/hw/ipmi/ipmi_bmc_sim.c
>> +++ b/hw/ipmi/ipmi_bmc_sim.c
>> @@ -81,6 +81,9 @@
>>   #define IPMI_CMD_ENTER_SDR_REP_UPD_MODE   0x2A
>>   #define IPMI_CMD_EXIT_SDR_REP_UPD_MODE    0x2B
>>   #define IPMI_CMD_RUN_INIT_AGENT           0x2C
>> +#define IPMI_CMD_GET_FRU_AREA_INFO        0x10
>> +#define IPMI_CMD_READ_FRU_DATA            0x11
>> +#define IPMI_CMD_WRITE_FRU_DATA           0x12
>>   #define IPMI_CMD_GET_SEL_INFO             0x40
>>   #define IPMI_CMD_GET_SEL_ALLOC_INFO       0x41
>>   #define IPMI_CMD_RESERVE_SEL              0x42
>> @@ -123,6 +126,14 @@ typedef struct IPMISdr {
>>       uint8_t overflow;
>>   } IPMISdr;
>>   +/* theoretically, the offset being 16bits, it should be 65536 */
>> +#define MAX_FRU_SIZE 256
>> +#define MAX_FRU_ID 32
>> +
>> +typedef struct IPMIFru {
>> +    uint8_t data[MAX_FRU_SIZE][MAX_FRU_ID];
>> +} IPMIFru;
> 
> Instead of a static table like this, I think it would be better to make this 
> configurable somehow.  I say this because I've never seen a system with 32 FRU 
> devices on a BMC, but I've seen plenty with FRU data larger than 256 bytes.  
> By default, 1 FRU device with 2048 bytes is pretty reasonable, I think.
> 
> I'm not exactly sure the best way to make it configurable. 

I guess we can use an object property to configure the numbers of FRU devices 
and start with a minimum of 1.

> I assume that you 
> need your platform code to be able to provide that information, and it could 
> be passed in as BMC configuration parameters.  The ability to load the FRU 
> data at startup is probably also necessary.

I will see what API we can provide after doing the above.

Thanks,

C. 

> -corey
> 
>> +
>>   typedef struct IPMISensor {
>>       uint8_t status;
>>       uint8_t reading;
>> @@ -206,6 +217,7 @@ struct IPMIBmcSim {
>>         IPMISel sel;
>>       IPMISdr sdr;
>> +    IPMIFru fru;
>>       IPMISensor sensors[MAX_SENSORS];
>>         /* Odd netfns are for responses, so we only need the even ones. */
>> @@ -1305,6 +1317,110 @@ static void get_sel_info(IPMIBmcSim *ibs,
>>       return;
>>   }
>>   +static void get_fru_area_info(IPMIBmcSim *ibs,
>> +                         uint8_t *cmd, unsigned int cmd_len,
>> +                         uint8_t *rsp, unsigned int *rsp_len,
>> +                         unsigned int max_rsp_len)
>> +{
>> +    uint8_t fruid;
>> +    uint16_t fru_entry_size;
>> +
>> +    IPMI_CHECK_CMD_LEN(3);
>> +
>> +    fruid = cmd[2];
>> +
>> +    if (fruid > MAX_FRU_ID) {
>> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
>> +        goto out;
>> +    }
>> +
>> +    fru_entry_size = MAX_FRU_SIZE;
>> +
>> +    IPMI_ADD_RSP_DATA(fru_entry_size & 0xff);
>> +    IPMI_ADD_RSP_DATA(fru_entry_size >> 8 & 0xff);
>> +    IPMI_ADD_RSP_DATA(0x0);
>> +out:
>> +    return;
>> +}
>> +
>> +#define min(x, y) ((x) < (y) ? (x) : (y))
>> +#define max(x, y) ((x) > (y) ? (x) : (y))
>> +
>> +static void read_fru_data(IPMIBmcSim *ibs,
>> +                         uint8_t *cmd, unsigned int cmd_len,
>> +                         uint8_t *rsp, unsigned int *rsp_len,
>> +                         unsigned int max_rsp_len)
>> +{
>> +    uint8_t fruid;
>> +    uint16_t offset;
>> +    int i;
>> +    uint8_t *fru_entry;
>> +    unsigned int count;
>> +
>> +    IPMI_CHECK_CMD_LEN(5);
>> +
>> +    fruid = cmd[2];
>> +    offset = (cmd[3] | cmd[4] << 8);
>> +
>> +    if (fruid > MAX_FRU_ID) {
>> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
>> +        goto out;
>> +    }
>> +
>> +    if (offset >= MAX_FRU_SIZE - 1) {
>> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
>> +        goto out;
>> +    }
>> +
>> +    fru_entry = ibs->fru.data[fruid];
>> +
>> +    count = min(cmd[5], MAX_FRU_SIZE - offset);
>> +
>> +    IPMI_ADD_RSP_DATA(count & 0xff);
>> +    for (i = 0; i < count; i++) {
>> +        IPMI_ADD_RSP_DATA(fru_entry[offset + i]);
>> +    }
>> +
>> + out:
>> +    return;
>> +}
>> +
>> +static void write_fru_data(IPMIBmcSim *ibs,
>> +                         uint8_t *cmd, unsigned int cmd_len,
>> +                         uint8_t *rsp, unsigned int *rsp_len,
>> +                         unsigned int max_rsp_len)
>> +{
>> +    uint8_t fruid;
>> +    uint16_t offset;
>> +    uint8_t *fru_entry;
>> +    unsigned int count;
>> +
>> +    IPMI_CHECK_CMD_LEN(5);
>> +
>> +    fruid = cmd[2];
>> +    offset = (cmd[3] | cmd[4] << 8);
>> +
>> +    if (fruid > MAX_FRU_ID) {
>> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
>> +        goto out;
>> +    }
>> +
>> +    if (offset >= MAX_FRU_SIZE - 1) {
>> +        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
>> +        goto out;
>> +    }
>> +
>> +    fru_entry = ibs->fru.data[fruid];
>> +
>> +    count = min(cmd_len - 5, MAX_FRU_SIZE - offset);
>> +
>> +    memcpy(fru_entry + offset, cmd + 5, count);
>> +
>> +    IPMI_ADD_RSP_DATA(count & 0xff);
>> + out:
>> +    return;
>> +}
>> +
>>   static void reserve_sel(IPMIBmcSim *ibs,
>>                           uint8_t *cmd, unsigned int cmd_len,
>>                           uint8_t *rsp, unsigned int *rsp_len,
>> @@ -1682,6 +1798,9 @@ static const IPMINetfn app_netfn = {
>>   };
>>     static const IPMICmdHandler storage_cmds[IPMI_NETFN_STORAGE_MAXCMD] = {
>> +    [IPMI_CMD_GET_FRU_AREA_INFO] = get_fru_area_info,
>> +    [IPMI_CMD_READ_FRU_DATA] = read_fru_data,
>> +    [IPMI_CMD_WRITE_FRU_DATA] = write_fru_data,
>>       [IPMI_CMD_GET_SDR_REP_INFO] = get_sdr_rep_info,
>>       [IPMI_CMD_RESERVE_SDR_REP] = reserve_sdr_rep,
>>       [IPMI_CMD_GET_SDR] = get_sdr,
>
diff mbox

Patch

diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c
index 5db94491b130..60586a67104e 100644
--- a/hw/ipmi/ipmi_bmc_sim.c
+++ b/hw/ipmi/ipmi_bmc_sim.c
@@ -81,6 +81,9 @@ 
 #define IPMI_CMD_ENTER_SDR_REP_UPD_MODE   0x2A
 #define IPMI_CMD_EXIT_SDR_REP_UPD_MODE    0x2B
 #define IPMI_CMD_RUN_INIT_AGENT           0x2C
+#define IPMI_CMD_GET_FRU_AREA_INFO        0x10
+#define IPMI_CMD_READ_FRU_DATA            0x11
+#define IPMI_CMD_WRITE_FRU_DATA           0x12
 #define IPMI_CMD_GET_SEL_INFO             0x40
 #define IPMI_CMD_GET_SEL_ALLOC_INFO       0x41
 #define IPMI_CMD_RESERVE_SEL              0x42
@@ -123,6 +126,14 @@  typedef struct IPMISdr {
     uint8_t overflow;
 } IPMISdr;
 
+/* theoretically, the offset being 16bits, it should be 65536 */
+#define MAX_FRU_SIZE 256
+#define MAX_FRU_ID 32
+
+typedef struct IPMIFru {
+    uint8_t data[MAX_FRU_SIZE][MAX_FRU_ID];
+} IPMIFru;
+
 typedef struct IPMISensor {
     uint8_t status;
     uint8_t reading;
@@ -206,6 +217,7 @@  struct IPMIBmcSim {
 
     IPMISel sel;
     IPMISdr sdr;
+    IPMIFru fru;
     IPMISensor sensors[MAX_SENSORS];
 
     /* Odd netfns are for responses, so we only need the even ones. */
@@ -1305,6 +1317,110 @@  static void get_sel_info(IPMIBmcSim *ibs,
     return;
 }
 
+static void get_fru_area_info(IPMIBmcSim *ibs,
+                         uint8_t *cmd, unsigned int cmd_len,
+                         uint8_t *rsp, unsigned int *rsp_len,
+                         unsigned int max_rsp_len)
+{
+    uint8_t fruid;
+    uint16_t fru_entry_size;
+
+    IPMI_CHECK_CMD_LEN(3);
+
+    fruid = cmd[2];
+
+    if (fruid > MAX_FRU_ID) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+
+    fru_entry_size = MAX_FRU_SIZE;
+
+    IPMI_ADD_RSP_DATA(fru_entry_size & 0xff);
+    IPMI_ADD_RSP_DATA(fru_entry_size >> 8 & 0xff);
+    IPMI_ADD_RSP_DATA(0x0);
+out:
+    return;
+}
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#define max(x, y) ((x) > (y) ? (x) : (y))
+
+static void read_fru_data(IPMIBmcSim *ibs,
+                         uint8_t *cmd, unsigned int cmd_len,
+                         uint8_t *rsp, unsigned int *rsp_len,
+                         unsigned int max_rsp_len)
+{
+    uint8_t fruid;
+    uint16_t offset;
+    int i;
+    uint8_t *fru_entry;
+    unsigned int count;
+
+    IPMI_CHECK_CMD_LEN(5);
+
+    fruid = cmd[2];
+    offset = (cmd[3] | cmd[4] << 8);
+
+    if (fruid > MAX_FRU_ID) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+
+    if (offset >= MAX_FRU_SIZE - 1) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+
+    fru_entry = ibs->fru.data[fruid];
+
+    count = min(cmd[5], MAX_FRU_SIZE - offset);
+
+    IPMI_ADD_RSP_DATA(count & 0xff);
+    for (i = 0; i < count; i++) {
+        IPMI_ADD_RSP_DATA(fru_entry[offset + i]);
+    }
+
+ out:
+    return;
+}
+
+static void write_fru_data(IPMIBmcSim *ibs,
+                         uint8_t *cmd, unsigned int cmd_len,
+                         uint8_t *rsp, unsigned int *rsp_len,
+                         unsigned int max_rsp_len)
+{
+    uint8_t fruid;
+    uint16_t offset;
+    uint8_t *fru_entry;
+    unsigned int count;
+
+    IPMI_CHECK_CMD_LEN(5);
+
+    fruid = cmd[2];
+    offset = (cmd[3] | cmd[4] << 8);
+
+    if (fruid > MAX_FRU_ID) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+
+    if (offset >= MAX_FRU_SIZE - 1) {
+        rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+        goto out;
+    }
+
+    fru_entry = ibs->fru.data[fruid];
+
+    count = min(cmd_len - 5, MAX_FRU_SIZE - offset);
+
+    memcpy(fru_entry + offset, cmd + 5, count);
+
+    IPMI_ADD_RSP_DATA(count & 0xff);
+ out:
+    return;
+}
+
 static void reserve_sel(IPMIBmcSim *ibs,
                         uint8_t *cmd, unsigned int cmd_len,
                         uint8_t *rsp, unsigned int *rsp_len,
@@ -1682,6 +1798,9 @@  static const IPMINetfn app_netfn = {
 };
 
 static const IPMICmdHandler storage_cmds[IPMI_NETFN_STORAGE_MAXCMD] = {
+    [IPMI_CMD_GET_FRU_AREA_INFO] = get_fru_area_info,
+    [IPMI_CMD_READ_FRU_DATA] = read_fru_data,
+    [IPMI_CMD_WRITE_FRU_DATA] = write_fru_data,
     [IPMI_CMD_GET_SDR_REP_INFO] = get_sdr_rep_info,
     [IPMI_CMD_RESERVE_SDR_REP] = reserve_sdr_rep,
     [IPMI_CMD_GET_SDR] = get_sdr,