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