| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU IPMI KCS emulation | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2015,2017 Corey Minyard, MontaVista Software, LLC | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
					
						
							|  |  |  |  * of this software and associated documentation files (the "Software"), to deal | 
					
						
							|  |  |  |  * in the Software without restriction, including without limitation the rights | 
					
						
							|  |  |  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
					
						
							|  |  |  |  * copies of the Software, and to permit persons to whom the Software is | 
					
						
							|  |  |  |  * furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The above copyright notice and this permission notice shall be included in | 
					
						
							|  |  |  |  * all copies or substantial portions of the Software. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
					
						
							|  |  |  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
					
						
							|  |  |  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
					
						
							|  |  |  |  * THE SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include "qemu/osdep.h"
 | 
					
						
							|  |  |  | #include "migration/vmstate.h"
 | 
					
						
							|  |  |  | #include "qemu/log.h"
 | 
					
						
							|  |  |  | #include "qapi/error.h"
 | 
					
						
							|  |  |  | #include "hw/ipmi/ipmi_kcs.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define IPMI_KCS_OBF_BIT        0
 | 
					
						
							|  |  |  | #define IPMI_KCS_IBF_BIT        1
 | 
					
						
							|  |  |  | #define IPMI_KCS_SMS_ATN_BIT    2
 | 
					
						
							|  |  |  | #define IPMI_KCS_CD_BIT         3
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define IPMI_KCS_OBF_MASK          (1 << IPMI_KCS_OBF_BIT)
 | 
					
						
							|  |  |  | #define IPMI_KCS_GET_OBF(d)        (((d) >> IPMI_KCS_OBF_BIT) & 0x1)
 | 
					
						
							|  |  |  | #define IPMI_KCS_SET_OBF(d, v)     (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \
 | 
					
						
							|  |  |  |                                        (((v) & 1) << IPMI_KCS_OBF_BIT)) | 
					
						
							|  |  |  | #define IPMI_KCS_IBF_MASK          (1 << IPMI_KCS_IBF_BIT)
 | 
					
						
							|  |  |  | #define IPMI_KCS_GET_IBF(d)        (((d) >> IPMI_KCS_IBF_BIT) & 0x1)
 | 
					
						
							|  |  |  | #define IPMI_KCS_SET_IBF(d, v)     (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \
 | 
					
						
							|  |  |  |                                        (((v) & 1) << IPMI_KCS_IBF_BIT)) | 
					
						
							|  |  |  | #define IPMI_KCS_SMS_ATN_MASK      (1 << IPMI_KCS_SMS_ATN_BIT)
 | 
					
						
							|  |  |  | #define IPMI_KCS_GET_SMS_ATN(d)    (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1)
 | 
					
						
							|  |  |  | #define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \
 | 
					
						
							|  |  |  |                                        (((v) & 1) << IPMI_KCS_SMS_ATN_BIT)) | 
					
						
							|  |  |  | #define IPMI_KCS_CD_MASK           (1 << IPMI_KCS_CD_BIT)
 | 
					
						
							|  |  |  | #define IPMI_KCS_GET_CD(d)         (((d) >> IPMI_KCS_CD_BIT) & 0x1)
 | 
					
						
							|  |  |  | #define IPMI_KCS_SET_CD(d, v)      (d) = (((d) & ~IPMI_KCS_CD_MASK) | \
 | 
					
						
							|  |  |  |                                        (((v) & 1) << IPMI_KCS_CD_BIT)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define IPMI_KCS_IDLE_STATE        0
 | 
					
						
							|  |  |  | #define IPMI_KCS_READ_STATE        1
 | 
					
						
							|  |  |  | #define IPMI_KCS_WRITE_STATE       2
 | 
					
						
							|  |  |  | #define IPMI_KCS_ERROR_STATE       3
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define IPMI_KCS_GET_STATE(d)    (((d) >> 6) & 0x3)
 | 
					
						
							|  |  |  | #define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define IPMI_KCS_ABORT_STATUS_CMD       0x60
 | 
					
						
							|  |  |  | #define IPMI_KCS_WRITE_START_CMD        0x61
 | 
					
						
							|  |  |  | #define IPMI_KCS_WRITE_END_CMD          0x62
 | 
					
						
							|  |  |  | #define IPMI_KCS_READ_CMD               0x68
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define IPMI_KCS_STATUS_NO_ERR          0x00
 | 
					
						
							|  |  |  | #define IPMI_KCS_STATUS_ABORTED_ERR     0x01
 | 
					
						
							|  |  |  | #define IPMI_KCS_STATUS_BAD_CC_ERR      0x02
 | 
					
						
							|  |  |  | #define IPMI_KCS_STATUS_LENGTH_ERR      0x06
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipmi_kcs_raise_irq(IPMIKCS *ik) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (ik->use_irq && ik->irqs_enabled && ik->raise_irq) { | 
					
						
							|  |  |  |         ik->raise_irq(ik); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipmi_kcs_lower_irq(IPMIKCS *ik) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (ik->lower_irq) { | 
					
						
							|  |  |  |         ik->lower_irq(ik); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SET_OBF() \
 | 
					
						
							|  |  |  |     do {                                                                      \ | 
					
						
							|  |  |  |         IPMI_KCS_SET_OBF(ik->status_reg, 1);                                  \ | 
					
						
							|  |  |  |         if (!ik->obf_irq_set) {                                               \ | 
					
						
							|  |  |  |             ik->obf_irq_set = 1;                                              \ | 
					
						
							|  |  |  |             if (!ik->atn_irq_set) {                                           \ | 
					
						
							|  |  |  |                 ipmi_kcs_raise_irq(ik);                                  \ | 
					
						
							|  |  |  |             }                                                                 \ | 
					
						
							|  |  |  |         }                                                                     \ | 
					
						
							|  |  |  |     } while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ik->do_wake = 1; | 
					
						
							|  |  |  |     while (ik->do_wake) { | 
					
						
							|  |  |  |         ik->do_wake = 0; | 
					
						
							|  |  |  |         iic->handle_if_event(ii); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipmi_kcs_handle_event(IPMIInterface *ii) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | 
					
						
							|  |  |  |     IPMIKCS *ik = iic->get_backend_data(ii); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) { | 
					
						
							|  |  |  |         if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) { | 
					
						
							|  |  |  |             ik->waiting_rsp++; /* Invalidate the message */ | 
					
						
							|  |  |  |             ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR; | 
					
						
							|  |  |  |             ik->outlen = 1; | 
					
						
							|  |  |  |             ik->outpos = 0; | 
					
						
							|  |  |  |             IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); | 
					
						
							|  |  |  |             SET_OBF(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         goto out; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (IPMI_KCS_GET_STATE(ik->status_reg)) { | 
					
						
							|  |  |  |     case IPMI_KCS_IDLE_STATE: | 
					
						
							|  |  |  |         if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) { | 
					
						
							|  |  |  |             IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE); | 
					
						
							|  |  |  |             ik->cmd_reg = -1; | 
					
						
							|  |  |  |             ik->write_end = 0; | 
					
						
							|  |  |  |             ik->inlen = 0; | 
					
						
							|  |  |  |             SET_OBF(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case IPMI_KCS_READ_STATE: | 
					
						
							|  |  |  |     handle_read: | 
					
						
							|  |  |  |         if (ik->outpos >= ik->outlen) { | 
					
						
							|  |  |  |             IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE); | 
					
						
							|  |  |  |             SET_OBF(); | 
					
						
							|  |  |  |         } else if (ik->data_in_reg == IPMI_KCS_READ_CMD) { | 
					
						
							|  |  |  |             ik->data_out_reg = ik->outmsg[ik->outpos]; | 
					
						
							|  |  |  |             ik->outpos++; | 
					
						
							|  |  |  |             SET_OBF(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; | 
					
						
							|  |  |  |             ik->outlen = 1; | 
					
						
							|  |  |  |             ik->outpos = 0; | 
					
						
							|  |  |  |             IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); | 
					
						
							|  |  |  |             SET_OBF(); | 
					
						
							|  |  |  |             goto out; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case IPMI_KCS_WRITE_STATE: | 
					
						
							|  |  |  |         if (ik->data_in_reg != -1) { | 
					
						
							|  |  |  |             /*
 | 
					
						
							|  |  |  |              * Don't worry about input overrun here, that will be | 
					
						
							|  |  |  |              * handled in the BMC. | 
					
						
							|  |  |  |              */ | 
					
						
							|  |  |  |             if (ik->inlen < sizeof(ik->inmsg)) { | 
					
						
							|  |  |  |                 ik->inmsg[ik->inlen] = ik->data_in_reg; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             ik->inlen++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (ik->write_end) { | 
					
						
							|  |  |  |             IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc); | 
					
						
							|  |  |  |             ik->outlen = 0; | 
					
						
							|  |  |  |             ik->write_end = 0; | 
					
						
							|  |  |  |             ik->outpos = 0; | 
					
						
							|  |  |  |             bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg), | 
					
						
							|  |  |  |                                ik->waiting_rsp); | 
					
						
							|  |  |  |             goto out_noibf; | 
					
						
							|  |  |  |         } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) { | 
					
						
							|  |  |  |             ik->cmd_reg = -1; | 
					
						
							|  |  |  |             ik->write_end = 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         SET_OBF(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case IPMI_KCS_ERROR_STATE: | 
					
						
							|  |  |  |         if (ik->data_in_reg != -1) { | 
					
						
							|  |  |  |             IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE); | 
					
						
							|  |  |  |             ik->data_in_reg = IPMI_KCS_READ_CMD; | 
					
						
							|  |  |  |             goto handle_read; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ik->cmd_reg != -1) { | 
					
						
							|  |  |  |         /* Got an invalid command */ | 
					
						
							|  |  |  |         ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; | 
					
						
							|  |  |  |         ik->outlen = 1; | 
					
						
							|  |  |  |         ik->outpos = 0; | 
					
						
							|  |  |  |         IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  out: | 
					
						
							|  |  |  |     ik->cmd_reg = -1; | 
					
						
							|  |  |  |     ik->data_in_reg = -1; | 
					
						
							|  |  |  |     IPMI_KCS_SET_IBF(ik->status_reg, 0); | 
					
						
							|  |  |  |  out_noibf: | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id, | 
					
						
							|  |  |  |                                 unsigned char *rsp, unsigned int rsp_len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | 
					
						
							|  |  |  |     IPMIKCS *ik = iic->get_backend_data(ii); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ik->waiting_rsp == msg_id) { | 
					
						
							|  |  |  |         ik->waiting_rsp++; | 
					
						
							|  |  |  |         if (rsp_len > sizeof(ik->outmsg)) { | 
					
						
							|  |  |  |             ik->outmsg[0] = rsp[0]; | 
					
						
							|  |  |  |             ik->outmsg[1] = rsp[1]; | 
					
						
							|  |  |  |             ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; | 
					
						
							|  |  |  |             ik->outlen = 3; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             memcpy(ik->outmsg, rsp, rsp_len); | 
					
						
							|  |  |  |             ik->outlen = rsp_len; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE); | 
					
						
							|  |  |  |         ik->data_in_reg = IPMI_KCS_READ_CMD; | 
					
						
							|  |  |  |         ipmi_kcs_signal(ik, ii); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IPMIInterface *ii = opaque; | 
					
						
							|  |  |  |     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | 
					
						
							|  |  |  |     IPMIKCS *ik = iic->get_backend_data(ii); | 
					
						
							|  |  |  |     uint32_t ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 13:18:07 -06:00
										 |  |  |     switch (addr & ik->size_mask) { | 
					
						
							| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  |     case 0: | 
					
						
							|  |  |  |         ret = ik->data_out_reg; | 
					
						
							|  |  |  |         IPMI_KCS_SET_OBF(ik->status_reg, 0); | 
					
						
							|  |  |  |         if (ik->obf_irq_set) { | 
					
						
							|  |  |  |             ik->obf_irq_set = 0; | 
					
						
							|  |  |  |             if (!ik->atn_irq_set) { | 
					
						
							|  |  |  |                 ipmi_kcs_lower_irq(ik); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2017-12-06 13:18:07 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  |     case 1: | 
					
						
							|  |  |  |         ret = ik->status_reg; | 
					
						
							|  |  |  |         if (ik->atn_irq_set) { | 
					
						
							|  |  |  |             ik->atn_irq_set = 0; | 
					
						
							|  |  |  |             if (!ik->obf_irq_set) { | 
					
						
							|  |  |  |                 ipmi_kcs_lower_irq(ik); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2017-12-06 13:18:07 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         ret = 0xff; | 
					
						
							| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val, | 
					
						
							|  |  |  |                                   unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IPMIInterface *ii = opaque; | 
					
						
							|  |  |  |     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | 
					
						
							|  |  |  |     IPMIKCS *ik = iic->get_backend_data(ii); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (IPMI_KCS_GET_IBF(ik->status_reg)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 13:18:07 -06:00
										 |  |  |     switch (addr & ik->size_mask) { | 
					
						
							| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  |     case 0: | 
					
						
							|  |  |  |         ik->data_in_reg = val; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 1: | 
					
						
							|  |  |  |         ik->cmd_reg = val; | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2017-12-06 13:18:07 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         /* Ignore. */ | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     IPMI_KCS_SET_IBF(ik->status_reg, 1); | 
					
						
							|  |  |  |     ipmi_kcs_signal(ik, ii); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const MemoryRegionOps ipmi_kcs_io_ops = { | 
					
						
							|  |  |  |     .read = ipmi_kcs_ioport_read, | 
					
						
							|  |  |  |     .write = ipmi_kcs_ioport_write, | 
					
						
							|  |  |  |     .impl = { | 
					
						
							|  |  |  |         .min_access_size = 1, | 
					
						
							|  |  |  |         .max_access_size = 1, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     .endianness = DEVICE_LITTLE_ENDIAN, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | 
					
						
							|  |  |  |     IPMIKCS *ik = iic->get_backend_data(ii); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IPMI_KCS_SET_SMS_ATN(ik->status_reg, val); | 
					
						
							|  |  |  |     if (val) { | 
					
						
							|  |  |  |         if (irq && !ik->atn_irq_set) { | 
					
						
							|  |  |  |             ik->atn_irq_set = 1; | 
					
						
							|  |  |  |             if (!ik->obf_irq_set) { | 
					
						
							|  |  |  |                 ipmi_kcs_raise_irq(ik); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         if (ik->atn_irq_set) { | 
					
						
							|  |  |  |             ik->atn_irq_set = 0; | 
					
						
							|  |  |  |             if (!ik->obf_irq_set) { | 
					
						
							|  |  |  |                 ipmi_kcs_lower_irq(ik); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | 
					
						
							|  |  |  |     IPMIKCS *ik = iic->get_backend_data(ii); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ik->irqs_enabled = val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 13:18:07 -06:00
										 |  |  | /* min_size must be a power of 2. */ | 
					
						
							|  |  |  | static void ipmi_kcs_init(IPMIInterface *ii, unsigned int min_size, | 
					
						
							|  |  |  |                           Error **errp) | 
					
						
							| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); | 
					
						
							|  |  |  |     IPMIKCS *ik = iic->get_backend_data(ii); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 13:18:07 -06:00
										 |  |  |     if (min_size == 0) { | 
					
						
							|  |  |  |         min_size = 2; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ik->size_mask = min_size - 1; | 
					
						
							| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  |     ik->io_length = 2; | 
					
						
							| 
									
										
										
										
											2017-12-06 13:18:07 -06:00
										 |  |  |     memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", | 
					
						
							|  |  |  |                           min_size); | 
					
						
							| 
									
										
										
										
											2017-12-06 07:34:24 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ipmi_kcs_vmstate_post_load(void *opaque, int version) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IPMIKCS *ik = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Make sure all the values are sane. */ | 
					
						
							|  |  |  |     if (ik->outpos >= MAX_IPMI_MSG_SIZE || ik->outlen >= MAX_IPMI_MSG_SIZE || | 
					
						
							|  |  |  |         ik->outpos >= ik->outlen) { | 
					
						
							|  |  |  |         qemu_log_mask(LOG_GUEST_ERROR, | 
					
						
							|  |  |  |                       "ipmi:kcs: vmstate transfer received bad out values: %d %d\n", | 
					
						
							|  |  |  |                       ik->outpos, ik->outlen); | 
					
						
							|  |  |  |         ik->outpos = 0; | 
					
						
							|  |  |  |         ik->outlen = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ik->inlen >= MAX_IPMI_MSG_SIZE) { | 
					
						
							|  |  |  |         qemu_log_mask(LOG_GUEST_ERROR, | 
					
						
							|  |  |  |                       "ipmi:kcs: vmstate transfer received bad in value: %d\n", | 
					
						
							|  |  |  |                       ik->inlen); | 
					
						
							|  |  |  |         ik->inlen = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool vmstate_kcs_before_version2(void *opaque, int version) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return version <= 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const VMStateDescription vmstate_IPMIKCS = { | 
					
						
							|  |  |  |     .name = TYPE_IPMI_INTERFACE_PREFIX "kcs", | 
					
						
							|  |  |  |     .version_id = 2, | 
					
						
							|  |  |  |     .minimum_version_id = 1, | 
					
						
							|  |  |  |     .post_load = ipmi_kcs_vmstate_post_load, | 
					
						
							|  |  |  |     .fields      = (VMStateField[]) { | 
					
						
							|  |  |  |         VMSTATE_BOOL(obf_irq_set, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_BOOL(atn_irq_set, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_UNUSED_TEST(vmstate_kcs_before_version2, 1), /* Was use_irq */ | 
					
						
							|  |  |  |         VMSTATE_BOOL(irqs_enabled, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_UINT32(outpos, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_UINT32_V(outlen, IPMIKCS, 2), | 
					
						
							|  |  |  |         VMSTATE_UINT8_ARRAY(outmsg, IPMIKCS, MAX_IPMI_MSG_SIZE), | 
					
						
							|  |  |  |         VMSTATE_UINT32_V(inlen, IPMIKCS, 2), | 
					
						
							|  |  |  |         VMSTATE_UINT8_ARRAY(inmsg, IPMIKCS, MAX_IPMI_MSG_SIZE), | 
					
						
							|  |  |  |         VMSTATE_BOOL(write_end, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_UINT8(status_reg, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_UINT8(data_out_reg, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_INT16(data_in_reg, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_INT16(cmd_reg, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_UINT8(waiting_rsp, IPMIKCS), | 
					
						
							|  |  |  |         VMSTATE_END_OF_LIST() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ipmi_kcs_get_fwinfo(IPMIKCS *ik, IPMIFwInfo *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     info->interface_name = "kcs"; | 
					
						
							|  |  |  |     info->interface_type = IPMI_SMBIOS_KCS; | 
					
						
							|  |  |  |     info->ipmi_spec_major_revision = 2; | 
					
						
							|  |  |  |     info->ipmi_spec_minor_revision = 0; | 
					
						
							|  |  |  |     info->base_address = ik->io_base; | 
					
						
							|  |  |  |     info->i2c_slave_address = ik->bmc->slave_addr; | 
					
						
							|  |  |  |     info->register_length = ik->io_length; | 
					
						
							|  |  |  |     info->register_spacing = 1; | 
					
						
							|  |  |  |     info->memspace = IPMI_MEMSPACE_IO; | 
					
						
							|  |  |  |     info->irq_type = IPMI_LEVEL_IRQ; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ipmi_kcs_class_init(IPMIInterfaceClass *iic) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     iic->init = ipmi_kcs_init; | 
					
						
							|  |  |  |     iic->set_atn = ipmi_kcs_set_atn; | 
					
						
							|  |  |  |     iic->handle_rsp = ipmi_kcs_handle_rsp; | 
					
						
							|  |  |  |     iic->handle_if_event = ipmi_kcs_handle_event; | 
					
						
							|  |  |  |     iic->set_irq_enable = ipmi_kcs_set_irq_enable; | 
					
						
							|  |  |  | } |