/* * NOTE: IPMI System Interface Selection * There is no system interface specified by the IPMI operation * region access. We try to select one system interface with ACPI * handle set. IPMI messages passed from the ACPI codes are sent * to this selected global IPMI system interface.
*/ struct acpi_ipmi_device *selected_smi; struct completion smi_selection_done;
};
struct acpi_ipmi_msg { struct list_head head;
/* * General speaking the addr type should be SI_ADDR_TYPE. And * the addr channel should be BMC. * In fact it can also be IPMB type. But we will have to * parse it from the Netfn command buffer. It is so complex * that it is skipped.
*/ struct ipmi_addr addr; long tx_msgid;
/* it is used to track whether the IPMI message is finished */ struct completion tx_complete;
struct kernel_ipmi_msg tx_message; int msg_done;
/* tx/rx data . And copy it from/to ACPI object buffer */
u8 data[ACPI_IPMI_MAX_MSG_LENGTH];
u8 rx_len;
/* * IPMI network function and command are encoded in the address * within the IPMI OpRegion; see ACPI 4.0, sec 5.5.2.4.3.
*/
msg->netfn = IPMI_OP_RGN_NETFN(address);
msg->cmd = IPMI_OP_RGN_CMD(address);
msg->data = tx_msg->data;
/* * value is the parameter passed by the IPMI opregion space handler. * It points to the IPMI request message buffer
*/
buffer = (struct acpi_ipmi_buffer *)value;
/* copy the tx message data */ if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
dev_WARN_ONCE(tx_msg->device->dev, true, "Unexpected request (msg len %d).\n",
buffer->length); return -EINVAL;
}
msg->data_len = buffer->length;
memcpy(tx_msg->data, buffer->data, msg->data_len);
/* * now the default type is SYSTEM_INTERFACE and channel type is BMC. * If the netfn is APP_REQUEST and the cmd is SEND_MESSAGE, * the addr type should be changed to IPMB. Then we will have to parse * the IPMI request message buffer to get the IPMB address. * If so, please fix me.
*/
tx_msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
tx_msg->addr.channel = IPMI_BMC_CHANNEL;
tx_msg->addr.data[0] = 0;
/* * value is also used as output parameter. It represents the response * IPMI message returned by IPMI command.
*/
buffer = (struct acpi_ipmi_buffer *)value;
/* * If the flag of msg_done is not set, it means that the IPMI command is * not executed correctly.
*/
buffer->status = msg->msg_done; if (msg->msg_done != ACPI_IPMI_OK) return;
/* * If the IPMI response message is obtained correctly, the status code * will be ACPI_IPMI_OK
*/
buffer->length = msg->rx_len;
memcpy(buffer->data, msg->data, msg->rx_len);
}
/* * NOTE: On-going ipmi_recv_msg * ipmi_msg_handler() may still be invoked by ipmi_si after * flushing. But it is safe to do a fast flushing on module_exit() * without waiting for all ipmi_recv_msg(s) to complete from * ipmi_msg_handler() as it is ensured by ipmi_si that all * ipmi_recv_msg(s) are freed after invoking ipmi_destroy_user().
*/
spin_lock_irqsave(&ipmi->tx_msg_lock, flags); while (!list_empty(&ipmi->tx_msg_list)) {
tx_msg = list_first_entry(&ipmi->tx_msg_list, struct acpi_ipmi_msg,
head);
list_del(&tx_msg->head);
spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
/* wake up the sleep thread on the Tx msg */
complete(&tx_msg->tx_complete);
acpi_ipmi_msg_put(tx_msg);
spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
}
spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
}
if (msg->user != ipmi_device->user_interface) {
dev_warn(dev, "Unexpected response is returned. returned user %p, expected user %p\n",
msg->user, ipmi_device->user_interface); goto out_msg;
}
if (!tx_msg) {
dev_warn(dev, "Unexpected response (msg id %ld) is returned.\n",
msg->msgid); goto out_msg;
}
/* copy the response data to Rx_data buffer */ if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
dev_WARN_ONCE(dev, true, "Unexpected response (msg len %d).\n",
msg->msg.data_len); goto out_comp;
}
/* response msg is an error msg */
msg->recv_type = IPMI_RESPONSE_RECV_TYPE; if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE &&
msg->msg.data_len == 1) { if (msg->msg.data[0] == IPMI_TIMEOUT_COMPLETION_CODE) {
dev_dbg_once(dev, "Unexpected response (timeout).\n");
tx_msg->msg_done = ACPI_IPMI_TIMEOUT;
} goto out_comp;
}
if (ipmi_device) {
ipmi_flush_tx_msg(ipmi_device);
acpi_ipmi_dev_put(ipmi_device);
}
}
/* * This is the IPMI opregion space handler. * @function: indicates the read/write. In fact as the IPMI message is driven * by command, only write is meaningful. * @address: This contains the netfn/command of IPMI request message. * @bits : not used. * @value : it is an in/out parameter. It points to the IPMI message buffer. * Before the IPMI message is sent, it represents the actual request * IPMI message. After the IPMI message is finished, it represents * the response IPMI message returned by IPMI command. * @handler_context: IPMI device context.
*/ static acpi_status
acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
u32 bits, acpi_integer *value, void *handler_context, void *region_context)
{ struct acpi_ipmi_msg *tx_msg; struct acpi_ipmi_device *ipmi_device; int err;
acpi_status status; unsignedlong flags;
/* * IPMI opregion message. * IPMI message is firstly written to the BMC and system software * can get the respsonse. So it is unmeaningful for the read access * of IPMI opregion.
*/ if ((function & ACPI_IO_MASK) == ACPI_READ) return AE_TYPE;
tx_msg = ipmi_msg_alloc(); if (!tx_msg) return AE_NOT_EXIST;
ipmi_device = tx_msg->device;
acpi_ipmi_msg_get(tx_msg);
mutex_lock(&driver_data.ipmi_lock); /* Do not add a tx_msg that can not be flushed. */ if (ipmi_device->dead) {
mutex_unlock(&driver_data.ipmi_lock);
ipmi_msg_release(tx_msg); return AE_NOT_EXIST;
}
spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
mutex_unlock(&driver_data.ipmi_lock);
err = ipmi_request_settime(ipmi_device->user_interface,
&tx_msg->addr,
tx_msg->tx_msgid,
&tx_msg->tx_message,
NULL, 0, 0, IPMI_TIMEOUT); if (err) {
status = AE_ERROR; goto out_msg;
}
wait_for_completion(&tx_msg->tx_complete);
acpi_format_ipmi_response(tx_msg, value);
status = AE_OK;
/* * When one smi_watcher is unregistered, it is only deleted * from the smi_watcher list. But the smi_gone callback function * is not called. So explicitly uninstall the ACPI IPMI oregion * handler and free it.
*/
mutex_lock(&driver_data.ipmi_lock); while (!list_empty(&driver_data.ipmi_devices)) {
ipmi_device = list_first_entry(&driver_data.ipmi_devices, struct acpi_ipmi_device,
head);
__ipmi_dev_kill(ipmi_device);
mutex_unlock(&driver_data.ipmi_lock);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.