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