qemu/hw/ppc/pnv_bmc.c
<<
>>
Prefs
   1/*
   2 * QEMU PowerNV, BMC related functions
   3 *
   4 * Copyright (c) 2016-2017, IBM Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License, version 2, as
   8 * published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include "qemu/osdep.h"
  20#include "qemu-common.h"
  21#include "qapi/error.h"
  22#include "target/ppc/cpu.h"
  23#include "qemu/log.h"
  24#include "hw/ipmi/ipmi.h"
  25#include "hw/ppc/fdt.h"
  26
  27#include "hw/ppc/pnv.h"
  28
  29#include <libfdt.h>
  30
  31/* TODO: include definition in ipmi.h */
  32#define IPMI_SDR_FULL_TYPE 1
  33
  34/*
  35 * OEM SEL Event data packet sent by BMC in response of a Read Event
  36 * Message Buffer command
  37 */
  38typedef struct OemSel {
  39    /* SEL header */
  40    uint8_t id[2];
  41    uint8_t type;
  42    uint8_t timestamp[4];
  43    uint8_t manuf_id[3];
  44
  45    /* OEM SEL data (6 bytes) follows */
  46    uint8_t netfun;
  47    uint8_t cmd;
  48    uint8_t data[4];
  49} OemSel;
  50
  51#define SOFT_OFF        0x00
  52#define SOFT_REBOOT     0x01
  53
  54static bool pnv_bmc_is_simulator(IPMIBmc *bmc)
  55{
  56    return object_dynamic_cast(OBJECT(bmc), TYPE_IPMI_BMC_SIMULATOR);
  57}
  58
  59static void pnv_gen_oem_sel(IPMIBmc *bmc, uint8_t reboot)
  60{
  61    /* IPMI SEL Event are 16 bytes long */
  62    OemSel sel = {
  63        .id        = { 0x55 , 0x55 },
  64        .type      = 0xC0, /* OEM */
  65        .manuf_id  = { 0x0, 0x0, 0x0 },
  66        .timestamp = { 0x0, 0x0, 0x0, 0x0 },
  67        .netfun    = 0x3A, /* IBM */
  68        .cmd       = 0x04, /* AMI OEM SEL Power Notification */
  69        .data      = { reboot, 0xFF, 0xFF, 0xFF },
  70    };
  71
  72    ipmi_bmc_gen_event(bmc, (uint8_t *) &sel, 0 /* do not log the event */);
  73}
  74
  75void pnv_bmc_powerdown(IPMIBmc *bmc)
  76{
  77    pnv_gen_oem_sel(bmc, SOFT_OFF);
  78}
  79
  80void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt)
  81{
  82    int offset;
  83    int i;
  84    const struct ipmi_sdr_compact *sdr;
  85    uint16_t nextrec;
  86
  87    if (!pnv_bmc_is_simulator(bmc)) {
  88        return;
  89    }
  90
  91    offset = fdt_add_subnode(fdt, 0, "bmc");
  92    _FDT(offset);
  93
  94    _FDT((fdt_setprop_string(fdt, offset, "name", "bmc")));
  95    offset = fdt_add_subnode(fdt, offset, "sensors");
  96    _FDT(offset);
  97
  98    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1)));
  99    _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0)));
 100
 101    for (i = 0; !ipmi_bmc_sdr_find(bmc, i, &sdr, &nextrec); i++) {
 102        int off;
 103        char *name;
 104
 105        if (sdr->header.rec_type != IPMI_SDR_COMPACT_TYPE &&
 106            sdr->header.rec_type != IPMI_SDR_FULL_TYPE) {
 107            continue;
 108        }
 109
 110        name = g_strdup_printf("sensor@%x", sdr->sensor_owner_number);
 111        off = fdt_add_subnode(fdt, offset, name);
 112        _FDT(off);
 113        g_free(name);
 114
 115        _FDT((fdt_setprop_cell(fdt, off, "reg", sdr->sensor_owner_number)));
 116        _FDT((fdt_setprop_string(fdt, off, "name", "sensor")));
 117        _FDT((fdt_setprop_string(fdt, off, "compatible", "ibm,ipmi-sensor")));
 118        _FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-reading-type",
 119                               sdr->reading_type)));
 120        _FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-id",
 121                               sdr->entity_id)));
 122        _FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-instance",
 123                               sdr->entity_instance)));
 124        _FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-type",
 125                               sdr->sensor_type)));
 126    }
 127}
 128
 129/*
 130 * HIOMAP protocol handler
 131 */
 132#define HIOMAP_C_RESET                  1
 133#define HIOMAP_C_GET_INFO               2
 134#define HIOMAP_C_GET_FLASH_INFO         3
 135#define HIOMAP_C_CREATE_READ_WINDOW     4
 136#define HIOMAP_C_CLOSE_WINDOW           5
 137#define HIOMAP_C_CREATE_WRITE_WINDOW    6
 138#define HIOMAP_C_MARK_DIRTY             7
 139#define HIOMAP_C_FLUSH                  8
 140#define HIOMAP_C_ACK                    9
 141#define HIOMAP_C_ERASE                  10
 142#define HIOMAP_C_DEVICE_NAME            11
 143#define HIOMAP_C_LOCK                   12
 144
 145#define BLOCK_SHIFT                     12 /* 4K */
 146
 147static uint16_t bytes_to_blocks(uint32_t bytes)
 148{
 149    return bytes >> BLOCK_SHIFT;
 150}
 151
 152static uint32_t blocks_to_bytes(uint16_t blocks)
 153{
 154    return blocks << BLOCK_SHIFT;
 155}
 156
 157static int hiomap_erase(PnvPnor *pnor, uint32_t offset, uint32_t size)
 158{
 159    MemTxResult result;
 160    int i;
 161
 162    for (i = 0; i < size / 4; i++) {
 163        result = memory_region_dispatch_write(&pnor->mmio, offset + i * 4,
 164                                              0xFFFFFFFF, MO_32,
 165                                              MEMTXATTRS_UNSPECIFIED);
 166        if (result != MEMTX_OK) {
 167            return -1;
 168        }
 169    }
 170    return 0;
 171}
 172
 173static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len,
 174                       RspBuffer *rsp)
 175{
 176    PnvPnor *pnor = PNV_PNOR(object_property_get_link(OBJECT(ibs), "pnor",
 177                                                      &error_abort));
 178    uint32_t pnor_size = pnor->size;
 179    uint32_t pnor_addr = PNOR_SPI_OFFSET;
 180    bool readonly = false;
 181
 182    rsp_buffer_push(rsp, cmd[2]);
 183    rsp_buffer_push(rsp, cmd[3]);
 184
 185    switch (cmd[2]) {
 186    case HIOMAP_C_MARK_DIRTY:
 187    case HIOMAP_C_FLUSH:
 188    case HIOMAP_C_ACK:
 189        break;
 190
 191    case HIOMAP_C_ERASE:
 192        if (hiomap_erase(pnor, blocks_to_bytes(cmd[5] << 8 | cmd[4]),
 193                        blocks_to_bytes(cmd[7] << 8 | cmd[6]))) {
 194            rsp_buffer_set_error(rsp, IPMI_CC_UNSPECIFIED);
 195        }
 196        break;
 197
 198    case HIOMAP_C_GET_INFO:
 199        rsp_buffer_push(rsp, 2);  /* Version 2 */
 200        rsp_buffer_push(rsp, BLOCK_SHIFT); /* block size */
 201        rsp_buffer_push(rsp, 0);  /* Timeout */
 202        rsp_buffer_push(rsp, 0);  /* Timeout */
 203        break;
 204
 205    case HIOMAP_C_GET_FLASH_INFO:
 206        rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF);
 207        rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8);
 208        rsp_buffer_push(rsp, 0x01);  /* erase size */
 209        rsp_buffer_push(rsp, 0x00);  /* erase size */
 210        break;
 211
 212    case HIOMAP_C_CREATE_READ_WINDOW:
 213        readonly = true;
 214        /* Fall through */
 215
 216    case HIOMAP_C_CREATE_WRITE_WINDOW:
 217        memory_region_set_readonly(&pnor->mmio, readonly);
 218        memory_region_set_enabled(&pnor->mmio, true);
 219
 220        rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) & 0xFF);
 221        rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) >> 8);
 222        rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF);
 223        rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8);
 224        rsp_buffer_push(rsp, 0x00); /* offset */
 225        rsp_buffer_push(rsp, 0x00); /* offset */
 226        break;
 227
 228    case HIOMAP_C_CLOSE_WINDOW:
 229        memory_region_set_enabled(&pnor->mmio, false);
 230        break;
 231
 232    case HIOMAP_C_DEVICE_NAME:
 233    case HIOMAP_C_RESET:
 234    case HIOMAP_C_LOCK:
 235    default:
 236        qemu_log_mask(LOG_GUEST_ERROR, "HIOMAP: unknown command %02X\n", cmd[2]);
 237        break;
 238    }
 239}
 240
 241#define HIOMAP   0x5a
 242
 243static const IPMICmdHandler hiomap_cmds[] = {
 244    [HIOMAP] = { hiomap_cmd, 3 },
 245};
 246
 247static const IPMINetfn hiomap_netfn = {
 248    .cmd_nums = ARRAY_SIZE(hiomap_cmds),
 249    .cmd_handlers = hiomap_cmds
 250};
 251
 252
 253void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor)
 254{
 255    if (!pnv_bmc_is_simulator(bmc)) {
 256        return;
 257    }
 258
 259    object_ref(OBJECT(pnor));
 260    object_property_add_const_link(OBJECT(bmc), "pnor", OBJECT(pnor));
 261
 262    /* Install the HIOMAP protocol handlers to access the PNOR */
 263    ipmi_sim_register_netfn(IPMI_BMC_SIMULATOR(bmc), IPMI_NETFN_OEM,
 264                            &hiomap_netfn);
 265}
 266
 267/*
 268 * Instantiate the machine BMC. PowerNV uses the QEMU internal
 269 * simulator but it could also be external.
 270 */
 271IPMIBmc *pnv_bmc_create(PnvPnor *pnor)
 272{
 273    Object *obj;
 274
 275    obj = object_new(TYPE_IPMI_BMC_SIMULATOR);
 276    qdev_realize(DEVICE(obj), NULL, &error_fatal);
 277    pnv_bmc_set_pnor(IPMI_BMC(obj), pnor);
 278
 279    return IPMI_BMC(obj);
 280}
 281
 282typedef struct ForeachArgs {
 283    const char *name;
 284    Object *obj;
 285} ForeachArgs;
 286
 287static int bmc_find(Object *child, void *opaque)
 288{
 289    ForeachArgs *args = opaque;
 290
 291    if (object_dynamic_cast(child, args->name)) {
 292        if (args->obj) {
 293            return 1;
 294        }
 295        args->obj = child;
 296    }
 297    return 0;
 298}
 299
 300IPMIBmc *pnv_bmc_find(Error **errp)
 301{
 302    ForeachArgs args = { TYPE_IPMI_BMC, NULL };
 303    int ret;
 304
 305    ret = object_child_foreach_recursive(object_get_root(), bmc_find, &args);
 306    if (ret) {
 307        error_setg(errp, "machine should have only one BMC device. "
 308                   "Use '-nodefaults'");
 309        return NULL;
 310    }
 311
 312    return args.obj ? IPMI_BMC(args.obj) : NULL;
 313}
 314