2 * QEMU IPMI SMBus (SSIF) emulation
4 * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
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"
31 #define TYPE_SMBUS_IPMI "smbus-ipmi"
32 #define SMBUS_IPMI(obj) OBJECT_CHECK(SMBusIPMIDevice, (obj), TYPE_SMBUS_IPMI)
34 #define SSIF_IPMI_REQUEST 2
35 #define SSIF_IPMI_MULTI_PART_REQUEST_START 6
36 #define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE 7
37 #define SSIF_IPMI_MULTI_PART_REQUEST_END 8
38 #define SSIF_IPMI_RESPONSE 3
39 #define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE 9
40 #define SSIF_IPMI_MULTI_PART_RETRY 0xa
42 #define MAX_SSIF_IPMI_MSG_SIZE 255
43 #define MAX_SSIF_IPMI_MSG_CHUNK 32
45 #define IPMI_GET_SYS_INTF_CAP_CMD 0x57
47 typedef struct SMBusIPMIDevice
{
52 uint8_t outmsg
[MAX_SSIF_IPMI_MSG_SIZE
];
56 /* Holds the SMBUS message currently being sent to the host. */
57 uint8_t outbuf
[MAX_SSIF_IPMI_MSG_CHUNK
+ 1]; /* len + message. */
60 uint8_t inmsg
[MAX_SSIF_IPMI_MSG_SIZE
];
64 * This is a response number that we send with the command to make
65 * sure that the response matches the command.
72 static void smbus_ipmi_handle_event(IPMIInterface
*ii
)
74 /* No interrupts, so nothing to do here. */
77 static void smbus_ipmi_handle_rsp(IPMIInterface
*ii
, uint8_t msg_id
,
78 unsigned char *rsp
, unsigned int rsp_len
)
80 SMBusIPMIDevice
*sid
= SMBUS_IPMI(ii
);
82 if (sid
->waiting_rsp
== msg_id
) {
85 if (rsp_len
> MAX_SSIF_IPMI_MSG_SIZE
) {
86 rsp
[2] = IPMI_CC_REQUEST_DATA_TRUNCATED
;
87 rsp_len
= MAX_SSIF_IPMI_MSG_SIZE
;
89 memcpy(sid
->outmsg
, rsp
, rsp_len
);
90 sid
->outlen
= rsp_len
;
96 static void smbus_ipmi_set_atn(IPMIInterface
*ii
, int val
, int irq
)
98 /* This is where PEC would go. */
101 static void smbus_ipmi_set_irq_enable(IPMIInterface
*ii
, int val
)
105 static void smbus_ipmi_send_msg(SMBusIPMIDevice
*sid
)
107 uint8_t *msg
= sid
->inmsg
;
108 uint32_t len
= sid
->inlen
;
109 IPMIBmcClass
*bk
= IPMI_BMC_GET_CLASS(sid
->bmc
);
115 if (msg
[0] == (IPMI_NETFN_APP
<< 2) && msg
[1] == IPMI_GET_SYS_INTF_CAP_CMD
)
117 /* We handle this ourself. */
118 sid
->outmsg
[0] = (IPMI_NETFN_APP
+ 1) << 2;
119 sid
->outmsg
[1] = msg
[1];
121 sid
->outmsg
[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID
;
123 } else if ((msg
[2] & 0x0f) != 0) {
124 sid
->outmsg
[2] = IPMI_CC_INVALID_DATA_FIELD
;
129 sid
->outmsg
[4] = (2 << 6); /* Multi-part supported. */
130 sid
->outmsg
[5] = MAX_SSIF_IPMI_MSG_SIZE
;
131 sid
->outmsg
[6] = MAX_SSIF_IPMI_MSG_SIZE
;
137 bk
->handle_command(sid
->bmc
, sid
->inmsg
, sid
->inlen
, sizeof(sid
->inmsg
),
141 static uint8_t ipmi_receive_byte(SMBusDevice
*dev
)
143 SMBusIPMIDevice
*sid
= SMBUS_IPMI(dev
);
145 if (sid
->outpos
>= sizeof(sid
->outbuf
)) {
149 return sid
->outbuf
[sid
->outpos
++];
152 static int ipmi_load_readbuf(SMBusIPMIDevice
*sid
)
154 unsigned int block
= sid
->currblk
, pos
, len
;
156 if (sid
->outlen
== 0) {
160 if (sid
->outlen
<= 32) {
164 sid
->outbuf
[0] = sid
->outlen
;
165 memcpy(sid
->outbuf
+ 1, sid
->outmsg
, sid
->outlen
);
174 memcpy(sid
->outbuf
+ 3, sid
->outmsg
, 30);
180 * Calculate the position in outmsg. 30 for the first block, 31
181 * for the rest of the blocks.
183 pos
= 30 + (block
- 1) * 31;
185 if (pos
>= sid
->outlen
) {
189 len
= sid
->outlen
- pos
;
191 /* More chunks after this. */
193 /* Blocks start at 0 for the first middle transaction. */
194 sid
->outbuf
[1] = block
- 1;
196 sid
->outbuf
[1] = 0xff; /* End of message marker. */
199 sid
->outbuf
[0] = len
+ 1;
200 memcpy(sid
->outbuf
+ 2, sid
->outmsg
+ pos
, len
);
205 static int ipmi_write_data(SMBusDevice
*dev
, uint8_t *buf
, uint8_t len
)
207 SMBusIPMIDevice
*sid
= SMBUS_IPMI(dev
);
212 /* length is guaranteed to be >= 1. */
216 /* Handle read request, which don't have any data in the write part. */
218 case SSIF_IPMI_RESPONSE
:
220 ret
= ipmi_load_readbuf(sid
);
223 case SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE
:
225 ret
= ipmi_load_readbuf(sid
);
228 case SSIF_IPMI_MULTI_PART_RETRY
:
230 sid
->currblk
= buf
[0];
231 ret
= ipmi_load_readbuf(sid
);
241 /* This should be a message write, make the length is there and correct. */
243 if (*buf
!= len
- 1 || *buf
> MAX_SSIF_IPMI_MSG_CHUNK
) {
244 return -1; /* Bogus message */
251 case SSIF_IPMI_REQUEST
:
254 case SSIF_IPMI_MULTI_PART_REQUEST_START
:
256 return -1; /* Bogus. */
258 memcpy(sid
->inmsg
, buf
, len
);
262 case SSIF_IPMI_MULTI_PART_REQUEST_END
:
265 case SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE
:
267 return -1; /* Bogus. */
269 if (sid
->inlen
+ len
> MAX_SSIF_IPMI_MSG_SIZE
) {
270 sid
->inlen
= 0; /* Discard the message. */
271 return -1; /* Bogus. */
275 * Special hack, a multi-part middle that is less than 32 bytes
276 * marks the end of a message. The specification is fairly
277 * confusing, so some systems to this, even sending a zero
278 * length end message to mark the end.
282 memcpy(sid
->inmsg
+ sid
->inlen
, buf
, len
);
287 if (send
&& sid
->inlen
) {
288 smbus_ipmi_send_msg(sid
);
294 static const VMStateDescription vmstate_smbus_ipmi
= {
295 .name
= TYPE_SMBUS_IPMI
,
297 .minimum_version_id
= 1,
298 .fields
= (VMStateField
[]) {
299 VMSTATE_SMBUS_DEVICE(parent
, SMBusIPMIDevice
),
300 VMSTATE_UINT8(waiting_rsp
, SMBusIPMIDevice
),
301 VMSTATE_UINT32(outlen
, SMBusIPMIDevice
),
302 VMSTATE_UINT32(currblk
, SMBusIPMIDevice
),
303 VMSTATE_UINT8_ARRAY(outmsg
, SMBusIPMIDevice
, MAX_SSIF_IPMI_MSG_SIZE
),
304 VMSTATE_UINT32(outpos
, SMBusIPMIDevice
),
305 VMSTATE_UINT8_ARRAY(outbuf
, SMBusIPMIDevice
,
306 MAX_SSIF_IPMI_MSG_CHUNK
+ 1),
307 VMSTATE_UINT32(inlen
, SMBusIPMIDevice
),
308 VMSTATE_UINT8_ARRAY(inmsg
, SMBusIPMIDevice
, MAX_SSIF_IPMI_MSG_SIZE
),
309 VMSTATE_END_OF_LIST()
313 static void smbus_ipmi_realize(DeviceState
*dev
, Error
**errp
)
315 SMBusIPMIDevice
*sid
= SMBUS_IPMI(dev
);
316 IPMIInterface
*ii
= IPMI_INTERFACE(dev
);
319 error_setg(errp
, "IPMI device requires a bmc attribute to be set");
323 sid
->uuid
= ipmi_next_uuid();
328 static void smbus_ipmi_init(Object
*obj
)
330 SMBusIPMIDevice
*sid
= SMBUS_IPMI(obj
);
332 ipmi_bmc_find_and_link(OBJECT(obj
), (Object
**) &sid
->bmc
);
335 static void smbus_ipmi_get_fwinfo(struct IPMIInterface
*ii
, IPMIFwInfo
*info
)
337 SMBusIPMIDevice
*sid
= SMBUS_IPMI(ii
);
339 info
->interface_name
= "smbus";
340 info
->interface_type
= IPMI_SMBIOS_SSIF
;
341 info
->ipmi_spec_major_revision
= 2;
342 info
->ipmi_spec_minor_revision
= 0;
343 info
->i2c_slave_address
= sid
->bmc
->slave_addr
;
344 info
->base_address
= sid
->parent
.i2c
.address
;
345 info
->memspace
= IPMI_MEMSPACE_SMBUS
;
346 info
->register_spacing
= 1;
347 info
->uuid
= sid
->uuid
;
350 static void smbus_ipmi_class_init(ObjectClass
*oc
, void *data
)
352 DeviceClass
*dc
= DEVICE_CLASS(oc
);
353 IPMIInterfaceClass
*iic
= IPMI_INTERFACE_CLASS(oc
);
354 SMBusDeviceClass
*sc
= SMBUS_DEVICE_CLASS(oc
);
356 sc
->receive_byte
= ipmi_receive_byte
;
357 sc
->write_data
= ipmi_write_data
;
358 dc
->vmsd
= &vmstate_smbus_ipmi
;
359 dc
->realize
= smbus_ipmi_realize
;
360 iic
->set_atn
= smbus_ipmi_set_atn
;
361 iic
->handle_rsp
= smbus_ipmi_handle_rsp
;
362 iic
->handle_if_event
= smbus_ipmi_handle_event
;
363 iic
->set_irq_enable
= smbus_ipmi_set_irq_enable
;
364 iic
->get_fwinfo
= smbus_ipmi_get_fwinfo
;
367 static const TypeInfo smbus_ipmi_info
= {
368 .name
= TYPE_SMBUS_IPMI
,
369 .parent
= TYPE_SMBUS_DEVICE
,
370 .instance_size
= sizeof(SMBusIPMIDevice
),
371 .instance_init
= smbus_ipmi_init
,
372 .class_init
= smbus_ipmi_class_init
,
373 .interfaces
= (InterfaceInfo
[]) {
374 { TYPE_IPMI_INTERFACE
},
379 static void smbus_ipmi_register_types(void)
381 type_register_static(&smbus_ipmi_info
);
384 type_init(smbus_ipmi_register_types
)