blob: 1fdf0a66b698512afe2635020905c9d65a04aff0 [file] [log] [blame]
Corey Minyard38033052015-04-01 15:11:10 -05001/*
2 * QEMU IPMI SMBus (SSIF) emulation
3 *
4 * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#include "qemu/osdep.h"
25#include "migration/vmstate.h"
26#include "hw/i2c/smbus_slave.h"
27#include "qapi/error.h"
28#include "qemu/error-report.h"
29#include "hw/ipmi/ipmi.h"
Eduardo Habkostdb1015e2020-09-03 16:43:22 -040030#include "qom/object.h"
Corey Minyard38033052015-04-01 15:11:10 -050031
32#define TYPE_SMBUS_IPMI "smbus-ipmi"
Eduardo Habkost80633962020-09-16 14:25:19 -040033OBJECT_DECLARE_SIMPLE_TYPE(SMBusIPMIDevice, SMBUS_IPMI)
Corey Minyard38033052015-04-01 15:11:10 -050034
35#define SSIF_IPMI_REQUEST 2
36#define SSIF_IPMI_MULTI_PART_REQUEST_START 6
37#define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE 7
38#define SSIF_IPMI_MULTI_PART_REQUEST_END 8
39#define SSIF_IPMI_RESPONSE 3
40#define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE 9
41#define SSIF_IPMI_MULTI_PART_RETRY 0xa
42
43#define MAX_SSIF_IPMI_MSG_SIZE 255
44#define MAX_SSIF_IPMI_MSG_CHUNK 32
45
46#define IPMI_GET_SYS_INTF_CAP_CMD 0x57
47
Eduardo Habkostdb1015e2020-09-03 16:43:22 -040048struct SMBusIPMIDevice {
Corey Minyard38033052015-04-01 15:11:10 -050049 SMBusDevice parent;
50
51 IPMIBmc *bmc;
52
53 uint8_t outmsg[MAX_SSIF_IPMI_MSG_SIZE];
54 uint32_t outlen;
55 uint32_t currblk;
56
57 /* Holds the SMBUS message currently being sent to the host. */
58 uint8_t outbuf[MAX_SSIF_IPMI_MSG_CHUNK + 1]; /* len + message. */
59 uint32_t outpos;
60
61 uint8_t inmsg[MAX_SSIF_IPMI_MSG_SIZE];
62 uint32_t inlen;
63
64 /*
65 * This is a response number that we send with the command to make
66 * sure that the response matches the command.
67 */
68 uint8_t waiting_rsp;
69
70 uint32_t uuid;
Eduardo Habkostdb1015e2020-09-03 16:43:22 -040071};
Corey Minyard38033052015-04-01 15:11:10 -050072
73static void smbus_ipmi_handle_event(IPMIInterface *ii)
74{
75 /* No interrupts, so nothing to do here. */
76}
77
78static void smbus_ipmi_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
79 unsigned char *rsp, unsigned int rsp_len)
80{
81 SMBusIPMIDevice *sid = SMBUS_IPMI(ii);
82
83 if (sid->waiting_rsp == msg_id) {
84 sid->waiting_rsp++;
85
86 if (rsp_len > MAX_SSIF_IPMI_MSG_SIZE) {
87 rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED;
88 rsp_len = MAX_SSIF_IPMI_MSG_SIZE;
89 }
90 memcpy(sid->outmsg, rsp, rsp_len);
91 sid->outlen = rsp_len;
92 sid->outpos = 0;
93 sid->currblk = 0;
94 }
95}
96
97static void smbus_ipmi_set_atn(IPMIInterface *ii, int val, int irq)
98{
99 /* This is where PEC would go. */
100}
101
102static void smbus_ipmi_set_irq_enable(IPMIInterface *ii, int val)
103{
104}
105
106static void smbus_ipmi_send_msg(SMBusIPMIDevice *sid)
107{
108 uint8_t *msg = sid->inmsg;
109 uint32_t len = sid->inlen;
110 IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(sid->bmc);
111
112 sid->outlen = 0;
113 sid->outpos = 0;
114 sid->currblk = 0;
115
116 if (msg[0] == (IPMI_NETFN_APP << 2) && msg[1] == IPMI_GET_SYS_INTF_CAP_CMD)
117 {
118 /* We handle this ourself. */
119 sid->outmsg[0] = (IPMI_NETFN_APP + 1) << 2;
120 sid->outmsg[1] = msg[1];
121 if (len < 3) {
122 sid->outmsg[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
123 sid->outlen = 3;
124 } else if ((msg[2] & 0x0f) != 0) {
125 sid->outmsg[2] = IPMI_CC_INVALID_DATA_FIELD;
126 sid->outlen = 3;
127 } else {
128 sid->outmsg[2] = 0;
129 sid->outmsg[3] = 0;
130 sid->outmsg[4] = (2 << 6); /* Multi-part supported. */
131 sid->outmsg[5] = MAX_SSIF_IPMI_MSG_SIZE;
132 sid->outmsg[6] = MAX_SSIF_IPMI_MSG_SIZE;
133 sid->outlen = 7;
134 }
135 return;
136 }
137
138 bk->handle_command(sid->bmc, sid->inmsg, sid->inlen, sizeof(sid->inmsg),
139 sid->waiting_rsp);
140}
141
142static uint8_t ipmi_receive_byte(SMBusDevice *dev)
143{
144 SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
145
146 if (sid->outpos >= sizeof(sid->outbuf)) {
147 return 0xff;
148 }
149
150 return sid->outbuf[sid->outpos++];
151}
152
153static int ipmi_load_readbuf(SMBusIPMIDevice *sid)
154{
155 unsigned int block = sid->currblk, pos, len;
156
157 if (sid->outlen == 0) {
158 return -1;
159 }
160
161 if (sid->outlen <= 32) {
162 if (block != 0) {
163 return -1;
164 }
165 sid->outbuf[0] = sid->outlen;
166 memcpy(sid->outbuf + 1, sid->outmsg, sid->outlen);
167 sid->outpos = 0;
168 return 0;
169 }
170
171 if (block == 0) {
172 sid->outbuf[0] = 32;
173 sid->outbuf[1] = 0;
174 sid->outbuf[2] = 1;
175 memcpy(sid->outbuf + 3, sid->outmsg, 30);
176 sid->outpos = 0;
177 return 0;
178 }
179
180 /*
181 * Calculate the position in outmsg. 30 for the first block, 31
182 * for the rest of the blocks.
183 */
184 pos = 30 + (block - 1) * 31;
185
186 if (pos >= sid->outlen) {
187 return -1;
188 }
189
190 len = sid->outlen - pos;
191 if (len > 31) {
192 /* More chunks after this. */
193 len = 31;
194 /* Blocks start at 0 for the first middle transaction. */
195 sid->outbuf[1] = block - 1;
196 } else {
197 sid->outbuf[1] = 0xff; /* End of message marker. */
198 }
199
200 sid->outbuf[0] = len + 1;
201 memcpy(sid->outbuf + 2, sid->outmsg + pos, len);
202 sid->outpos = 0;
203 return 0;
204}
205
206static int ipmi_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len)
207{
208 SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
209 bool send = false;
210 uint8_t cmd;
211 int ret = 0;
212
213 /* length is guaranteed to be >= 1. */
214 cmd = *buf++;
215 len--;
216
217 /* Handle read request, which don't have any data in the write part. */
218 switch (cmd) {
219 case SSIF_IPMI_RESPONSE:
220 sid->currblk = 0;
221 ret = ipmi_load_readbuf(sid);
222 break;
223
224 case SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE:
225 sid->currblk++;
226 ret = ipmi_load_readbuf(sid);
227 break;
228
229 case SSIF_IPMI_MULTI_PART_RETRY:
230 if (len >= 1) {
231 sid->currblk = buf[0];
232 ret = ipmi_load_readbuf(sid);
233 } else {
234 ret = -1;
235 }
236 break;
237
238 default:
239 break;
240 }
241
242 /* This should be a message write, make the length is there and correct. */
243 if (len >= 1) {
244 if (*buf != len - 1 || *buf > MAX_SSIF_IPMI_MSG_CHUNK) {
245 return -1; /* Bogus message */
246 }
247 buf++;
248 len--;
249 }
250
251 switch (cmd) {
252 case SSIF_IPMI_REQUEST:
253 send = true;
254 /* FALLTHRU */
255 case SSIF_IPMI_MULTI_PART_REQUEST_START:
256 if (len < 2) {
257 return -1; /* Bogus. */
258 }
259 memcpy(sid->inmsg, buf, len);
260 sid->inlen = len;
261 break;
262
263 case SSIF_IPMI_MULTI_PART_REQUEST_END:
264 send = true;
265 /* FALLTHRU */
266 case SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE:
267 if (!sid->inlen) {
268 return -1; /* Bogus. */
269 }
270 if (sid->inlen + len > MAX_SSIF_IPMI_MSG_SIZE) {
271 sid->inlen = 0; /* Discard the message. */
272 return -1; /* Bogus. */
273 }
274 if (len < 32) {
275 /*
276 * Special hack, a multi-part middle that is less than 32 bytes
277 * marks the end of a message. The specification is fairly
278 * confusing, so some systems to this, even sending a zero
279 * length end message to mark the end.
280 */
281 send = true;
282 }
283 memcpy(sid->inmsg + sid->inlen, buf, len);
284 sid->inlen += len;
285 break;
286 }
287
288 if (send && sid->inlen) {
289 smbus_ipmi_send_msg(sid);
290 }
291
292 return ret;
293}
294
295static const VMStateDescription vmstate_smbus_ipmi = {
296 .name = TYPE_SMBUS_IPMI,
297 .version_id = 1,
298 .minimum_version_id = 1,
299 .fields = (VMStateField[]) {
300 VMSTATE_SMBUS_DEVICE(parent, SMBusIPMIDevice),
301 VMSTATE_UINT8(waiting_rsp, SMBusIPMIDevice),
302 VMSTATE_UINT32(outlen, SMBusIPMIDevice),
303 VMSTATE_UINT32(currblk, SMBusIPMIDevice),
304 VMSTATE_UINT8_ARRAY(outmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE),
305 VMSTATE_UINT32(outpos, SMBusIPMIDevice),
306 VMSTATE_UINT8_ARRAY(outbuf, SMBusIPMIDevice,
307 MAX_SSIF_IPMI_MSG_CHUNK + 1),
308 VMSTATE_UINT32(inlen, SMBusIPMIDevice),
309 VMSTATE_UINT8_ARRAY(inmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE),
310 VMSTATE_END_OF_LIST()
311 }
312};
313
314static void smbus_ipmi_realize(DeviceState *dev, Error **errp)
315{
316 SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
317 IPMIInterface *ii = IPMI_INTERFACE(dev);
318
319 if (!sid->bmc) {
320 error_setg(errp, "IPMI device requires a bmc attribute to be set");
321 return;
322 }
323
324 sid->uuid = ipmi_next_uuid();
325
326 sid->bmc->intf = ii;
327}
328
329static void smbus_ipmi_init(Object *obj)
330{
331 SMBusIPMIDevice *sid = SMBUS_IPMI(obj);
332
Philippe Mathieu-Daudé688ffbb2020-05-12 09:00:19 +0200333 ipmi_bmc_find_and_link(obj, (Object **) &sid->bmc);
Corey Minyard38033052015-04-01 15:11:10 -0500334}
335
336static void smbus_ipmi_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info)
337{
338 SMBusIPMIDevice *sid = SMBUS_IPMI(ii);
339
340 info->interface_name = "smbus";
341 info->interface_type = IPMI_SMBIOS_SSIF;
342 info->ipmi_spec_major_revision = 2;
343 info->ipmi_spec_minor_revision = 0;
344 info->i2c_slave_address = sid->bmc->slave_addr;
345 info->base_address = sid->parent.i2c.address;
346 info->memspace = IPMI_MEMSPACE_SMBUS;
347 info->register_spacing = 1;
348 info->uuid = sid->uuid;
349}
350
351static void smbus_ipmi_class_init(ObjectClass *oc, void *data)
352{
353 DeviceClass *dc = DEVICE_CLASS(oc);
354 IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
355 SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(oc);
356
357 sc->receive_byte = ipmi_receive_byte;
358 sc->write_data = ipmi_write_data;
359 dc->vmsd = &vmstate_smbus_ipmi;
360 dc->realize = smbus_ipmi_realize;
361 iic->set_atn = smbus_ipmi_set_atn;
362 iic->handle_rsp = smbus_ipmi_handle_rsp;
363 iic->handle_if_event = smbus_ipmi_handle_event;
364 iic->set_irq_enable = smbus_ipmi_set_irq_enable;
365 iic->get_fwinfo = smbus_ipmi_get_fwinfo;
366}
367
368static const TypeInfo smbus_ipmi_info = {
369 .name = TYPE_SMBUS_IPMI,
370 .parent = TYPE_SMBUS_DEVICE,
371 .instance_size = sizeof(SMBusIPMIDevice),
372 .instance_init = smbus_ipmi_init,
373 .class_init = smbus_ipmi_class_init,
374 .interfaces = (InterfaceInfo[]) {
375 { TYPE_IPMI_INTERFACE },
376 { }
377 }
378};
379
380static void smbus_ipmi_register_types(void)
381{
382 type_register_static(&smbus_ipmi_info);
383}
384
385type_init(smbus_ipmi_register_types)