qemu/hw/smbios/smbios_type_38.c
<<
>>
Prefs
   1/*
   2 * IPMI SMBIOS firmware handling
   3 *
   4 * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   7 * See the COPYING file in the top-level directory.
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "hw/ipmi/ipmi.h"
  12#include "hw/firmware/smbios.h"
  13#include "qemu/error-report.h"
  14#include "smbios_build.h"
  15
  16/* SMBIOS type 38 - IPMI */
  17struct smbios_type_38 {
  18    struct smbios_structure_header header;
  19    uint8_t interface_type;
  20    uint8_t ipmi_spec_revision;
  21    uint8_t i2c_slave_address;
  22    uint8_t nv_storage_device_address;
  23    uint64_t base_address;
  24    uint8_t base_address_modifier;
  25    uint8_t interrupt_number;
  26} QEMU_PACKED;
  27
  28static void smbios_build_one_type_38(IPMIFwInfo *info)
  29{
  30    uint64_t baseaddr = info->base_address;
  31    SMBIOS_BUILD_TABLE_PRE(38, 0x3000, true);
  32
  33    t->interface_type = info->interface_type;
  34    t->ipmi_spec_revision = ((info->ipmi_spec_major_revision << 4)
  35                             | info->ipmi_spec_minor_revision);
  36    t->i2c_slave_address = info->i2c_slave_address;
  37    t->nv_storage_device_address = 0;
  38
  39    assert(info->ipmi_spec_minor_revision <= 15);
  40    assert(info->ipmi_spec_major_revision <= 15);
  41
  42    /* or 1 to set it to I/O space */
  43    switch (info->memspace) {
  44    case IPMI_MEMSPACE_IO:
  45        baseaddr |= 1;
  46        break;
  47    case IPMI_MEMSPACE_MEM32:
  48    case IPMI_MEMSPACE_MEM64:
  49        break;
  50    case IPMI_MEMSPACE_SMBUS:
  51        baseaddr <<= 1;
  52        break;
  53    }
  54
  55    t->base_address = cpu_to_le64(baseaddr);
  56
  57    t->base_address_modifier = 0;
  58    if (info->irq_type == IPMI_LEVEL_IRQ) {
  59        t->base_address_modifier |= 1;
  60    }
  61    switch (info->register_spacing) {
  62    case 1:
  63        break;
  64    case 4:
  65        t->base_address_modifier |= 1 << 6;
  66        break;
  67    case 16:
  68        t->base_address_modifier |= 2 << 6;
  69        break;
  70    default:
  71        error_report("IPMI register spacing %d is not compatible with"
  72                     " SMBIOS, ignoring this entry.", info->register_spacing);
  73        return;
  74    }
  75    t->interrupt_number = info->interrupt_number;
  76
  77    SMBIOS_BUILD_TABLE_POST;
  78}
  79
  80static void smbios_add_ipmi_devices(BusState *bus)
  81{
  82    BusChild *kid;
  83
  84    QTAILQ_FOREACH(kid, &bus->children,  sibling) {
  85        DeviceState *dev = kid->child;
  86        Object *obj = object_dynamic_cast(OBJECT(dev), TYPE_IPMI_INTERFACE);
  87        BusState *childbus;
  88
  89        if (obj) {
  90            IPMIInterface *ii;
  91            IPMIInterfaceClass *iic;
  92            IPMIFwInfo info;
  93
  94            ii = IPMI_INTERFACE(obj);
  95            iic = IPMI_INTERFACE_GET_CLASS(obj);
  96            memset(&info, 0, sizeof(info));
  97            if (!iic->get_fwinfo) {
  98                continue;
  99            }
 100            iic->get_fwinfo(ii, &info);
 101            smbios_build_one_type_38(&info);
 102            continue;
 103        }
 104
 105        QLIST_FOREACH(childbus, &dev->child_bus, sibling) {
 106            smbios_add_ipmi_devices(childbus);
 107        }
 108    }
 109}
 110
 111void smbios_build_type_38_table(void)
 112{
 113    BusState *bus;
 114
 115    bus = sysbus_get_default();
 116    if (bus) {
 117        smbios_add_ipmi_devices(bus);
 118    }
 119}
 120