/* * ipmi_msghandler.c * * Incoming and outgoing message routing for an IPMI interface. * * Author: MontaVista Software, Inc. * Corey Minyard * source@mvista.com * * Copyright 2002 MontaVista Software Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PFX "IPMI message handler: " #define IPMI_DRIVER_VERSION "39.2" static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); static int ipmi_init_msghandler(void); static int initialized; #ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_ipmi_root; #endif /* CONFIG_PROC_FS */ /* Remain in auto-maintenance mode for this amount of time (in ms). */ #define IPMI_MAINTENANCE_MODE_TIMEOUT 30000 #define MAX_EVENTS_IN_QUEUE 25 /* * Don't let a message sit in a queue forever, always time it with at lest * the max message timer. This is in milliseconds. */ #define MAX_MSG_TIMEOUT 60000 /* * The main "user" data structure. */ struct ipmi_user { struct list_head link; /* Set to "0" when the user is destroyed. */ int valid; struct kref refcount; /* The upper layer that handles receive messages. */ struct ipmi_user_hndl *handler; void *handler_data; /* The interface this user is bound to. */ ipmi_smi_t intf; /* Does this interface receive IPMI events? */ int gets_events; }; struct cmd_rcvr { struct list_head link; ipmi_user_t user; unsigned char netfn; unsigned char cmd; unsigned int chans; /* * This is used to form a linked lised during mass deletion. * Since this is in an RCU list, we cannot use the link above * or change any data until the RCU period completes. So we * use this next variable during mass deletion so we can have * a list and don't have to wait and restart the search on * every individual deletion of a command. */ struct cmd_rcvr *next; }; struct seq_table { unsigned int inuse : 1; unsigned int broadcast : 1; unsigned long timeout; unsigned long orig_timeout; unsigned int retries_left; /* * To verify on an incoming send message response that this is * the message that the response is for, we keep a sequence id * and increment it every time we send a message. */ long seqid; /* * This is held so we can properly respond to the message on a * timeout, and it is used to hold the temporary data for * retransmission, too. */ struct ipmi_recv_msg *recv_msg; }; /* * Store the information in a msgid (long) to allow us to find a * sequence table entry from the msgid. */ #define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff)) #define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \ do { \ seq = ((msgid >> 26) & 0x3f); \ seqid = (msgid & 0x3fffff); \ } while (0) #define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff) struct ipmi_channel { unsigned char medium; unsigned char protocol; /* * My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, * but may be changed by the user. */ unsigned char address; /* * My LUN. This should generally stay the SMS LUN, but just in * case... */ unsigned char lun; }; #ifdef CONFIG_PROC_FS struct ipmi_proc_entry { char *name; struct ipmi_proc_entry *next; }; #endif struct bmc_device { struct platform_device *dev; struct ipmi_device_id id; unsigned char guid[16]; int guid_set; struct kref refcount; /* bmc device attributes */ struct device_attribute device_id_attr; struct device_attribute provides_dev_sdrs_attr; struct device_attribute revision_attr; struct device_attribute firmware_rev_attr; struct device_attribute version_attr; struct device_attribute add_dev_support_attr; struct device_attribute manufacturer_id_attr; struct device_attribute product_id_attr; struct device_attribute guid_attr; struct device_attribute aux_firmware_rev_attr; }; /* * Various statistics for IPMI, these index stats[] in the ipmi_smi * structure. */ enum ipmi_stat_indexes { /* Commands we got from the user that were invalid. */ IPMI_STAT_sent_invalid_commands = 0, /* Commands we sent to the MC. */ IPMI_STAT_sent_local_commands, /* Responses from the MC that were delivered to a user. */ IPMI_STAT_handled_local_responses, /* Responses from the MC that were not delivered to a user. */ IPMI_STAT_unhandled_local_responses, /* Commands we sent out to the IPMB bus. */ IPMI_STAT_sent_ipmb_commands, /* Commands sent on the IPMB that had errors on the SEND CMD */ IPMI_STAT_sent_ipmb_command_errs, /* Each retransmit increments this count. */ IPMI_STAT_retransmitted_ipmb_commands, /* * When a message times out (runs out of retransmits) this is * incremented. */ IPMI_STAT_timed_out_ipmb_commands, /* * This is like above, but for broadcasts. Broadcasts are * *not* included in the above count (they are expected to * time out). */ IPMI_STAT_timed_out_ipmb_broadcasts, /* Responses I have sent to the IPMB bus. */ IPMI_STAT_sent_ipmb_responses, /* The response was delivered to the user. */ IPMI_STAT_handled_ipmb_responses, /* The response had invalid data in it. */ IPMI_STAT_invalid_ipmb_responses, /* The response didn't have anyone waiting for it. */ IPMI_STAT_unhandled_ipmb_responses, /* Commands we sent out to the IPMB bus. */ IPMI_STAT_sent_lan_commands, /* Commands sent on the IPMB that had errors on the SEND CMD */ IPMI_STAT_sent_lan_command_errs, /* Each retransmit increments this count. */ IPMI_STAT_retransmitted_lan_commands, /* * When a message times out (runs out of retransmits) this is * incremented. */ IPMI_STAT_timed_out_lan_commands, /* Responses I have sent to the IPMB bus. */ IPMI_STAT_sent_lan_responses, /* The response was delivered to the user. */ IPMI_STAT_handled_lan_responses, /* The response had invalid data in it. */ IPMI_STAT_invalid_lan_responses, /* The response didn't have anyone waiting for it. */ IPMI_STAT_unhandled_lan_responses, /* The command was delivered to the user. */ IPMI_STAT_handled_commands, /* The command had invalid data in it. */ IPMI_STAT_invalid_commands, /* The command didn't have anyone waiting for it. */ IPMI_STAT_unhandled_commands, /* Invalid data in an event. */ IPMI_STAT_invalid_events, /* Events that were received with the proper format. */ IPMI_STAT_events, /* This *must* remain last, add new values above this. */ IPMI_NUM_STATS }; #define IPMI_IPMB_NUM_SEQ 64 #define IPMI_MAX_CHANNELS 16 struct ipmi_smi { /* What interface number are we? */ int intf_num; struct kref refcount; /* Used for a list of interfaces. */ struct list_head link; /* * The list of upper layers that are using me. seq_lock * protects this. */ struct list_head users; /* Information to supply to users. */ unsigned char ipmi_version_major; unsigned char ipmi_version_minor; /* Used for wake ups at startup. */ wait_queue_head_t waitq; struct bmc_device *bmc; char *my_dev_name; char *sysfs_name; /* * This is the lower-layer's sender routine. Note that you * must either be holding the ipmi_interfaces_mutex or be in * an umpreemptible region to use this. You must fetch the * value into a local variable and make sure it is not NULL. */ struct ipmi_smi_handlers *handlers; void *send_info; #ifdef CONFIG_PROC_FS /* A list of proc entries for this interface. */ struct mutex proc_entry_lock; struct ipmi_proc_entry *proc_entries; #endif /* Driver-model device for the system interface. */ struct device *si_dev; /* * A table of sequence numbers for this interface. We use the * sequence numbers for IPMB messages that go out of the * interface to match them up with their responses. A routine * is called periodically to time the items in this list. */ spinlock_t seq_lock; struct seq_table seq_table[IPMI_IPMB_NUM_SEQ]; int curr_seq; /* * Messages that were delayed for some reason (out of memory, * for instance), will go in here to be processed later in a * periodic timer interrupt. */ spinlock_t waiting_msgs_lock; struct list_head waiting_msgs; /* * The list of command receivers that are registered for commands * on this interface. */ struct mutex cmd_rcvrs_mutex; struct list_head cmd_rcvrs; /* * Events that were queues because no one was there to receive * them. */ spinlock_t events_lock; /* For dealing with event stuff. */ struct list_head waiting_events; unsigned int waiting_events_count; /* How many events in queue? */ char delivering_events; char event_msg_printed; /* * The event receiver for my BMC, only really used at panic * shutdown as a place to store this. */ unsigned char event_receiver; unsigned char event_receiver_lun; unsigned char local_sel_device; unsigned char local_event_generator; /* For handling of maintenance mode. */ int maintenance_mode; int maintenance_mode_enable; int auto_maintenance_timeout; spinlock_t maintenance_mode_lock; /* Used in a timer... */ /* * A cheap hack, if this is non-null and a message to an * interface comes in with a NULL user, call this routine with * it. Note that the message will still be freed by the * caller. This only works on the system interface. */ void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg); /* * When we are scanning the channels for an SMI, this will * tell which channel we are scanning. */ int curr_channel; /* Channel information */ struct ipmi_channel channels[IPMI_MAX_CHANNELS]; /* Proc FS stuff. */ struct proc_dir_entry *proc_dir; char proc_dir_name[10]; atomic_t stats[IPMI_NUM_STATS]; /* * run_to_completion duplicate of smb_info, smi_info * and ipmi_serial_info structures. Used to decrease numbers of * parameters passed by "low" level IPMI code. */ int run_to_completion; }; #define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) /** * The driver model view of the IPMI messaging driver. */ static struct device_driver ipmidriver = { .name = "ipmi", .bus = &platform_bus_type }; static DEFINE_MUTEX(ipmidriver_mutex); static LIST_HEAD(ipmi_interfaces); static DEFINE_MUTEX(ipmi_interfaces_mutex); /* * List of watchers that want to know when smi's are added and deleted. */ static LIST_HEAD(smi_watchers); static DEFINE_MUTEX(smi_watchers_mutex); #define ipmi_inc_stat(intf, stat) \ atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat]) #define ipmi_get_stat(intf, stat) \ ((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat])) static void free_recv_msg_list(struct list_head *q) { struct ipmi_recv_msg *msg, *msg2; list_for_each_entry_safe(msg, msg2, q, link) { list_del(&msg->link); ipmi_free_recv_msg(msg); } } static void free_smi_msg_list(struct list_head *q) { struct ipmi_smi_msg *msg, *msg2; list_for_each_entry_safe(msg, msg2, q, link) { list_del(&msg->link); ipmi_free_smi_msg(msg); } } static void clean_up_interface_data(ipmi_smi_t intf) { int i; struct cmd_rcvr *rcvr, *rcvr2; struct list_head list; free_smi_msg_list(&intf->waiting_msgs); free_recv_msg_list(&intf->waiting_events); /* * Wholesale remove all the entries from the list in the * interface and wait for RCU to know that none are in use. */ mutex_lock(&intf->cmd_rcvrs_mutex); INIT_LIST_HEAD(&list); list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu); mutex_unlock(&intf->cmd_rcvrs_mutex); list_for_each_entry_safe(rcvr, rcvr2, &list, link) kfree(rcvr); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { if ((intf->seq_table[i].inuse) && (intf->seq_table[i].recv_msg)) ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } } static void intf_free(struct kref *ref) { ipmi_smi_t intf = container_of(ref, struct ipmi_smi, refcount); clean_up_interface_data(intf); kfree(intf); } struct watcher_entry { int intf_num; ipmi_smi_t intf; struct list_head link; }; int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) { ipmi_smi_t intf; LIST_HEAD(to_deliver); struct watcher_entry *e, *e2; mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); /* Build a list of things to deliver. */ list_for_each_entry(intf, &ipmi_interfaces, link) { if (intf->intf_num == -1) continue; e = kmalloc(sizeof(*e), GFP_KERNEL); if (!e) goto out_err; kref_get(&intf->refcount); e->intf = intf; e->intf_num = intf->intf_num; list_add_tail(&e->link, &to_deliver); } /* We will succeed, so add it to the list. */ list_add(&watcher->link, &smi_watchers); mutex_unlock(&ipmi_interfaces_mutex); list_for_each_entry_safe(e, e2, &to_deliver, link) { list_del(&e->link); watcher->new_smi(e->intf_num, e->intf->si_dev); kref_put(&e->intf->refcount, intf_free); kfree(e); } mutex_unlock(&smi_watchers_mutex); return 0; out_err: mutex_unlock(&ipmi_interfaces_mutex); mutex_unlock(&smi_watchers_mutex); list_for_each_entry_safe(e, e2, &to_deliver, link) { list_del(&e->link); kref_put(&e->intf->refcount, intf_free); kfree(e); } return -ENOMEM; } EXPORT_SYMBOL(ipmi_smi_watcher_register); int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) { mutex_lock(&smi_watchers_mutex); list_del(&(watcher->link)); mutex_unlock(&smi_watchers_mutex); return 0; } EXPORT_SYMBOL(ipmi_smi_watcher_unregister); /* * Must be called with smi_watchers_mutex held. */ static void call_smi_watchers(int i, struct device *dev) { struct ipmi_smi_watcher *w; list_for_each_entry(w, &smi_watchers, link) { if (try_module_get(w->owner)) { w->new_smi(i, dev); module_put(w->owner); } } } static int ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) { if (addr1->addr_type != addr2->addr_type) return 0; if (addr1->channel != addr2->channel) return 0; if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { struct ipmi_system_interface_addr *smi_addr1 = (struct ipmi_system_interface_addr *) addr1; struct ipmi_system_interface_addr *smi_addr2 = (struct ipmi_system_interface_addr *) addr2; return (smi_addr1->lun == smi_addr2->lun); } if ((addr1->addr_type == IPMI_IPMB_ADDR_TYPE) || (addr1->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) { struct ipmi_ipmb_addr *ipmb_addr1 = (struct ipmi_ipmb_addr *) addr1; struct ipmi_ipmb_addr *ipmb_addr2 = (struct ipmi_ipmb_addr *) addr2; return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr) && (ipmb_addr1->lun == ipmb_addr2->lun)); } if (addr1->addr_type == IPMI_LAN_ADDR_TYPE) { struct ipmi_lan_addr *lan_addr1 = (struct ipmi_lan_addr *) addr1; struct ipmi_lan_addr *lan_addr2 = (struct ipmi_lan_addr *) addr2; return ((lan_addr1->remote_SWID == lan_addr2->remote_SWID) && (lan_addr1->local_SWID == lan_addr2->local_SWID) && (lan_addr1->session_handle == lan_addr2->session_handle) && (lan_addr1->lun == lan_addr2->lun)); } return 1; } int ipmi_validate_addr(struct ipmi_addr *addr, int len) { if (len < sizeof(struct ipmi_system_interface_addr)) return -EINVAL; if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { if (addr->channel != IPMI_BMC_CHANNEL) return -EINVAL; return 0; } if ((addr->channel == IPMI_BMC_CHANNEL) || (addr->channel >= IPMI_MAX_CHANNELS) || (addr->channel < 0)) return -EINVAL; if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) { if (len < sizeof(struct ipmi_ipmb_addr)) return -EINVAL; return 0; } if (addr->addr_type == IPMI_LAN_ADDR_TYPE) { if (len < sizeof(struct ipmi_lan_addr)) return -EINVAL; return 0; } return -EINVAL; } EXPORT_SYMBOL(ipmi_validate_addr); unsigned int ipmi_addr_length(int addr_type) { if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) return sizeof(struct ipmi_system_interface_addr); if ((addr_type == IPMI_IPMB_ADDR_TYPE) || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) return sizeof(struct ipmi_ipmb_addr); if (addr_type == IPMI_LAN_ADDR_TYPE) return sizeof(struct ipmi_lan_addr); return 0; } EXPORT_SYMBOL(ipmi_addr_length); static void deliver_response(struct ipmi_recv_msg *msg) { if (!msg->user) { ipmi_smi_t intf = msg->user_msg_data; /* Special handling for NULL users. */ if (intf->null_user_handler) { intf->null_user_handler(intf, msg); ipmi_inc_stat(intf, handled_local_responses); } else { /* No handler, so give up. */ ipmi_inc_stat(intf, unhandled_local_responses); } ipmi_free_recv_msg(msg); } else { ipmi_user_t user = msg->user; user->handler->ipmi_recv_hndl(msg, user->handler_data); } } static void deliver_err_response(struct ipmi_recv_msg *msg, int err) { msg->recv_type = IPMI_RESPONSE_RECV_TYPE; msg->msg_data[0] = err; msg->msg.netfn |= 1; /* Convert to a response. */ msg->msg.data_len = 1; msg->msg.data = msg->msg_data; deliver_response(msg); } /* * Find the next sequence number not being used and add the given * message with the given timeout to the sequence table. This must be * called with the interface's seq_lock held. */ static int intf_next_seq(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, unsigned long timeout, int retries, int broadcast, unsigned char *seq, long *seqid) { int rv = 0; unsigned int i; for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; i = (i+1)%IPMI_IPMB_NUM_SEQ) { if (!intf->seq_table[i].inuse) break; } if (!intf->seq_table[i].inuse) { intf->seq_table[i].recv_msg = recv_msg; /* * Start with the maximum timeout, when the send response * comes in we will start the real timer. */ intf->seq_table[i].timeout = MAX_MSG_TIMEOUT; intf->seq_table[i].orig_timeout = timeout; intf->seq_table[i].retries_left = retries; intf->seq_table[i].broadcast = broadcast; intf->seq_table[i].inuse = 1; intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid); *seq = i; *seqid = intf->seq_table[i].seqid; intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ; } else { rv = -EAGAIN; } return rv; } /* * Return the receive message for the given sequence number and * release the sequence number so it can be reused. Some other data * is passed in to be sure the message matches up correctly (to help * guard against message coming in after their timeout and the * sequence number being reused). */ static int intf_find_seq(ipmi_smi_t intf, unsigned char seq, short channel, unsigned char cmd, unsigned char netfn, struct ipmi_addr *addr, struct ipmi_recv_msg **recv_msg) { int rv = -ENODEV; unsigned long flags; if (seq >= IPMI_IPMB_NUM_SEQ) return -EINVAL; spin_lock_irqsave(&(intf->seq_lock), flags); if (intf->seq_table[seq].inuse) { struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg; if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd) && (msg->msg.netfn == netfn) && (ipmi_addr_equal(addr, &(msg->addr)))) { *recv_msg = msg; intf->seq_table[seq].inuse = 0; rv = 0; } } spin_unlock_irqrestore(&(intf->seq_lock), flags); return rv; } /* Start the timer for a specific sequence table entry. */ static int intf_start_seq_timer(ipmi_smi_t intf, long msgid) { int rv = -ENODEV; unsigned long flags; unsigned char seq; unsigned long seqid; GET_SEQ_FROM_MSGID(msgid, seq, seqid); spin_lock_irqsave(&(intf->seq_lock), flags); /* * We do this verification because the user can be deleted * while a message is outstanding. */ if ((intf->seq_table[seq].inuse) && (intf->seq_table[seq].seqid == seqid)) { struct seq_table *ent = &(intf->seq_table[seq]); ent->timeout = ent->orig_timeout; rv = 0; } spin_unlock_irqrestore(&(intf->seq_lock), flags); return rv; } /* Got an error for the send message for a specific sequence number. */ static int intf_err_seq(ipmi_smi_t intf, long msgid, unsigned int err) { int rv = -ENODEV; unsigned long flags; unsigned char seq; unsigned long seqid; struct ipmi_recv_msg *msg = NULL; GET_SEQ_FROM_MSGID(msgid, seq, seqid); spin_lock_irqsave(&(intf->seq_lock), flags); /* * We do this verification because the user can be deleted * while a message is outstanding. */ if ((intf->seq_table[seq].inuse) && (intf->seq_table[seq].seqid == seqid)) { struct seq_table *ent = &(intf->seq_table[seq]); ent->inuse = 0; msg = ent->recv_msg; rv = 0; } spin_unlock_irqrestore(&(intf->seq_lock), flags); if (msg) deliver_err_response(msg, err); return rv; } int ipmi_create_user(unsigned int if_num, struct ipmi_user_hndl *handler, void *handler_data, ipmi_user_t *user) { unsigned long flags; ipmi_user_t new_user; int rv = 0; ipmi_smi_t intf; /* * There is no module usecount here, because it's not * required. Since this can only be used by and called from * other modules, they will implicitly use this module, and * thus this can't be removed unless the other modules are * removed. */ if (handler == NULL) return -EINVAL; /* * Make sure the driver is actually initialized, this handles * problems with initialization order. */ if (!initialized) { rv = ipmi_init_msghandler(); if (rv) return rv; /* * The init code doesn't return an error if it was turned * off, but it won't initialize. Check that. */ if (!initialized) return -ENODEV; } new_user = kmalloc(sizeof(*new_user), GFP_KERNEL); if (!new_user) return -ENOMEM; mutex_lock(&ipmi_interfaces_mutex); list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { if (intf->intf_num == if_num) goto found; } /* Not found, return an error */ rv = -EINVAL; goto out_kfree; found: /* Note that each existing user holds a refcount to the interface. */ kref_get(&intf->refcount); kref_init(&new_user->refcount); new_user->handler = handler; new_user->handler_data = handler_data; new_user->intf = intf; new_user->gets_events = 0; if (!try_module_get(intf->handlers->owner)) { rv = -ENODEV; goto out_kref; } if (intf->handlers->inc_usecount) { rv = intf->handlers->inc_usecount(intf->send_info); if (rv) { module_put(intf->handlers->owner); goto out_kref; } } /* * Hold the lock so intf->handlers is guaranteed to be good * until now */ mutex_unlock(&ipmi_interfaces_mutex); new_user->valid = 1; spin_lock_irqsave(&intf->seq_lock, flags); list_add_rcu(&new_user->link, &intf->users); spin_unlock_irqrestore(&intf->seq_lock, flags); *user = new_user; return 0; out_kref: kref_put(&intf->refcount, intf_free); out_kfree: mutex_unlock(&ipmi_interfaces_mutex); kfree(new_user); return rv; } EXPORT_SYMBOL(ipmi_create_user); static void free_user(struct kref *ref) { ipmi_user_t user = container_of(ref, struct ipmi_user, refcount); kfree(user); } int ipmi_destroy_user(ipmi_user_t user) { ipmi_smi_t intf = user->intf; int i; unsigned long flags; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; user->valid = 0; /* Remove the user from the interface's sequence table. */ spin_lock_irqsave(&intf->seq_lock, flags); list_del_rcu(&user->link); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { if (intf->seq_table[i].inuse && (intf->seq_table[i].recv_msg->user == user)) { intf->seq_table[i].inuse = 0; ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } } spin_unlock_irqrestore(&intf->seq_lock, flags); /* * Remove the user from the command receiver's table. First * we build a list of everything (not using the standard link, * since other things may be using it till we do * synchronize_rcu()) then free everything in that list. */ mutex_lock(&intf->cmd_rcvrs_mutex); list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { if (rcvr->user == user) { list_del_rcu(&rcvr->link); rcvr->next = rcvrs; rcvrs = rcvr; } } mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; rcvrs = rcvr->next; kfree(rcvr); } mutex_lock(&ipmi_interfaces_mutex); if (intf->handlers) { module_put(intf->handlers->owner); if (intf->handlers->dec_usecount) intf->handlers->dec_usecount(intf->send_info); } mutex_unlock(&ipmi_interfaces_mutex); kref_put(&intf->refcount, intf_free); kref_put(&user->refcount, free_user); return 0; } EXPORT_SYMBOL(ipmi_destroy_user); void ipmi_get_version(ipmi_user_t user, unsigned char *major, unsigned char *minor) { *major = user->intf->ipmi_version_major; *minor = user->intf->ipmi_version_minor; } EXPORT_SYMBOL(ipmi_get_version); int ipmi_set_my_address(ipmi_user_t user, unsigned int channel, unsigned char address) { if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; user->intf->channels[channel].address = address; return 0; } EXPORT_SYMBOL(ipmi_set_my_address); int ipmi_get_my_address(ipmi_user_t user, unsigned int channel, unsigned char *address) { if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; *address = user->intf->channels[channel].address; return 0; } EXPORT_SYMBOL(ipmi_get_my_address); int ipmi_set_my_LUN(ipmi_user_t user, unsigned int channel, unsigned char LUN) { if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; user->intf->channels[channel].lun = LUN & 0x3; return 0; } EXPORT_SYMBOL(ipmi_set_my_LUN); int ipmi_get_my_LUN(ipmi_user_t user, unsigned int channel, unsigned char *address) { if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; *address = user->intf->channels[channel].lun; return 0; } EXPORT_SYMBOL(ipmi_get_my_LUN); int ipmi_get_maintenance_mode(ipmi_user_t user) { int mode; unsigned long flags; spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags); mode = user->intf->maintenance_mode; spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags); return mode; } EXPORT_SYMBOL(ipmi_get_maintenance_mode); static void maintenance_mode_update(ipmi_smi_t intf) { if (intf->handlers->set_maintenance_mode) intf->handlers->set_maintenance_mode( intf->send_info, intf->maintenance_mode_enable); } int ipmi_set_maintenance_mode(ipmi_user_t user, int mode) { int rv = 0; unsigned long flags; ipmi_smi_t intf = user->intf; spin_lock_irqsave(&intf->maintenance_mode_lock, flags); if (intf->maintenance_mode != mode) { switch (mode) { case IPMI_MAINTENANCE_MODE_AUTO: intf->maintenance_mode = mode; intf->maintenance_mode_enable = (intf->auto_maintenance_timeout > 0); break; case IPMI_MAINTENANCE_MODE_OFF: intf->maintenance_mode = mode; intf->maintenance_mode_enable = 0; break; case IPMI_MAINTENANCE_MODE_ON: intf->maintenance_mode = mode; intf->maintenance_mode_enable = 1; break; default: rv = -EINVAL; goto out_unlock; } maintenance_mode_update(intf); } out_unlock: spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); return rv; } EXPORT_SYMBOL(ipmi_set_maintenance_mode); int ipmi_set_gets_events(ipmi_user_t user, int val) { unsigned long flags; ipmi_smi_t intf = user->intf; struct ipmi_recv_msg *msg, *msg2; struct list_head msgs; INIT_LIST_HEAD(&msgs); spin_lock_irqsave(&intf->events_lock, flags); user->gets_events = val; if (intf->delivering_events) /* * Another thread is delivering events for this, so * let it handle any new events. */ goto out; /* Deliver any queued events. */ while (user->gets_events && !list_empty(&intf->waiting_events)) { list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) list_move_tail(&msg->link, &msgs); intf->waiting_events_count = 0; if (intf->event_msg_printed) { printk(KERN_WARNING PFX "Event queue no longer" " full\n"); intf->event_msg_printed = 0; } intf->delivering_events = 1; spin_unlock_irqrestore(&intf->events_lock, flags); list_for_each_entry_safe(msg, msg2, &msgs, link) { msg->user = user; kref_get(&user->refcount); deliver_response(msg); } spin_lock_irqsave(&intf->events_lock, flags); intf->delivering_events = 0; } out: spin_unlock_irqrestore(&intf->events_lock, flags); return 0; } EXPORT_SYMBOL(ipmi_set_gets_events); static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, unsigned char netfn, unsigned char cmd, unsigned char chan) { struct cmd_rcvr *rcvr; list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) && (rcvr->chans & (1 << chan))) return rcvr; } return NULL; } static int is_cmd_rcvr_exclusive(ipmi_smi_t intf, unsigned char netfn, unsigned char cmd, unsigned int chans) { struct cmd_rcvr *rcvr; list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) && (rcvr->chans & chans)) return 0; } return 1; } int ipmi_register_for_cmd(ipmi_user_t user, unsigned char netfn, unsigned char cmd, unsigned int chans) { ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; int rv = 0; rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL); if (!rcvr) return -ENOMEM; rcvr->cmd = cmd; rcvr->netfn = netfn; rcvr->chans = chans; rcvr->user = user; mutex_lock(&intf->cmd_rcvrs_mutex); /* Make sure the command/netfn is not already registered. */ if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) { rv = -EBUSY; goto out_unlock; } list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); out_unlock: mutex_unlock(&intf->cmd_rcvrs_mutex); if (rv) kfree(rcvr); return rv; } EXPORT_SYMBOL(ipmi_register_for_cmd); int ipmi_unregister_for_cmd(ipmi_user_t user, unsigned char netfn, unsigned char cmd, unsigned int chans) { ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; int i, rv = -ENOENT; mutex_lock(&intf->cmd_rcvrs_mutex); for (i = 0; i < IPMI_NUM_CHANNELS; i++) { if (((1 << i) & chans) == 0) continue; rcvr = find_cmd_rcvr(intf, netfn, cmd, i); if (rcvr == NULL) continue; if (rcvr->user == user) { rv = 0; rcvr->chans &= ~chans; if (rcvr->chans == 0) { list_del_rcu(&rcvr->link); rcvr->next = rcvrs; rcvrs = rcvr; } } } mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; rcvrs = rcvr->next; kfree(rcvr); } return rv; } EXPORT_SYMBOL(ipmi_unregister_for_cmd); static unsigned char ipmb_checksum(unsigned char *data, int size) { unsigned char csum = 0; for (; size > 0; size--, data++) csum += *data; return -csum; } static inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg, struct kernel_ipmi_msg *msg, struct ipmi_ipmb_addr *ipmb_addr, long msgid, unsigned char ipmb_seq, int broadcast, unsigned char source_address, unsigned char source_lun) { int i = broadcast; /* Format the IPMB header data. */ smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); smi_msg->data[1] = IPMI_SEND_MSG_CMD; smi_msg->data[2] = ipmb_addr->channel; if (broadcast) smi_msg->data[3] = 0; smi_msg->data[i+3] = ipmb_addr->slave_addr; smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3); smi_msg->data[i+5] = ipmb_checksum(&(smi_msg->data[i+3]), 2); smi_msg->data[i+6] = source_address; smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun; smi_msg->data[i+8] = msg->cmd; /* Now tack on the data to the message. */ if (msg->data_len > 0) memcpy(&(smi_msg->data[i+9]), msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 9; /* Now calculate the checksum and tack it on. */ smi_msg->data[i+smi_msg->data_size] = ipmb_checksum(&(smi_msg->data[i+6]), smi_msg->data_size-6); /* * Add on the checksum size and the offset from the * broadcast. */ smi_msg->data_size += 1 + i; smi_msg->msgid = msgid; } static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg, struct kernel_ipmi_msg *msg, struct ipmi_lan_addr *lan_addr, long msgid, unsigned char ipmb_seq, unsigned char source_lun) { /* Format the IPMB header data. */ smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); smi_msg->data[1] = IPMI_SEND_MSG_CMD; smi_msg->data[2] = lan_addr->channel; smi_msg->data[3] = lan_addr->session_handle; smi_msg->data[4] = lan_addr->remote_SWID; smi_msg->data[5] = (msg->netfn << 2) | (lan_addr->lun & 0x3); smi_msg->data[6] = ipmb_checksum(&(smi_msg->data[4]), 2); smi_msg->data[7] = lan_addr->local_SWID; smi_msg->data[8] = (ipmb_seq << 2) | source_lun; smi_msg->data[9] = msg->cmd; /* Now tack on the data to the message. */ if (msg->data_len > 0) memcpy(&(smi_msg->data[10]), msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 10; /* Now calculate the checksum and tack it on. */ smi_msg->data[smi_msg->data_size] = ipmb_checksum(&(smi_msg->data[7]), smi_msg->data_size-7); /* * Add on the checksum size and the offset from the * broadcast. */ smi_msg->data_size += 1; smi_msg->msgid = msgid; } /* * Separate from ipmi_request so that the user does not have to be * supplied in certain circumstances (mainly at panic time). If * messages are supplied, they will be freed, even if an error * occurs. */ static int i_ipmi_request(ipmi_user_t user, ipmi_smi_t intf, struct ipmi_addr *addr, long msgid, struct kernel_ipmi_msg *msg, void *user_msg_data, void *supplied_smi, struct ipmi_recv_msg *supplied_recv, int priority, unsigned char source_address, unsigned char source_lun, int retries, unsigned int retry_time_ms) { int rv = 0; struct ipmi_smi_msg *smi_msg; struct ipmi_recv_msg *recv_msg; unsigned long flags; struct ipmi_smi_handlers *handlers; if (supplied_recv) recv_msg = supplied_recv; else { recv_msg = ipmi_alloc_recv_msg(); if (recv_msg == NULL) return -ENOMEM; } recv_msg->user_msg_data = user_msg_data; if (supplied_smi) smi_msg = (struct ipmi_smi_msg *) supplied_smi; else { smi_msg = ipmi_alloc_smi_msg(); if (smi_msg == NULL) { ipmi_free_recv_msg(recv_msg); return -ENOMEM; } } rcu_read_lock(); handlers = intf->handlers; if (!handlers) { rv = -ENODEV; goto out_err; } recv_msg->user = user; if (user) kref_get(&user->refcount); recv_msg->msgid = msgid; /* * Store the message to send in the receive message so timeout * responses can get the proper response data. */ recv_msg->msg = *msg; if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { struct ipmi_system_interface_addr *smi_addr; if (msg->netfn & 1) { /* Responses are not allowed to the SMI. */ rv = -EINVAL; goto out_err; } smi_addr = (struct ipmi_system_interface_addr *) addr; if (smi_addr->lun > 3) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr)); if ((msg->netfn == IPMI_NETFN_APP_REQUEST) && ((msg->cmd == IPMI_SEND_MSG_CMD) || (msg->cmd == IPMI_GET_MSG_CMD) || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) { /* * We don't let the user do these, since we manage * the sequence numbers. */ ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } if (((msg->netfn == IPMI_NETFN_APP_REQUEST) && ((msg->cmd == IPMI_COLD_RESET_CMD) || (msg->cmd == IPMI_WARM_RESET_CMD))) || (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST)) { spin_lock_irqsave(&intf->maintenance_mode_lock, flags); intf->auto_maintenance_timeout = IPMI_MAINTENANCE_MODE_TIMEOUT; if (!intf->maintenance_mode && !intf->maintenance_mode_enable) { intf->maintenance_mode_enable = 1; maintenance_mode_update(intf); } spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); } if ((msg->data_len + 2) > IPMI_MAX_MSG_LENGTH) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3); smi_msg->data[1] = msg->cmd; smi_msg->msgid = msgid; smi_msg->user_data = recv_msg; if (msg->data_len > 0) memcpy(&(smi_msg->data[2]), msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 2; ipmi_inc_stat(intf, sent_local_commands); } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) { struct ipmi_ipmb_addr *ipmb_addr; unsigned char ipmb_seq; long seqid; int broadcast = 0; if (addr->channel >= IPMI_MAX_CHANNELS) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } if (intf->channels[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } if (retries < 0) { if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) retries = 0; /* Don't retry broadcasts. */ else retries = 4; } if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) { /* * Broadcasts add a zero at the beginning of the * message, but otherwise is the same as an IPMB * address. */ addr->addr_type = IPMI_IPMB_ADDR_TYPE; broadcast = 1; } /* Default to 1 second retries. */ if (retry_time_ms == 0) retry_time_ms = 1000; /* * 9 for the header and 1 for the checksum, plus * possibly one for the broadcast. */ if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } ipmb_addr = (struct ipmi_ipmb_addr *) addr; if (ipmb_addr->lun > 3) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr)); if (recv_msg->msg.netfn & 0x1) { /* * It's a response, so use the user's sequence * from msgid. */ ipmi_inc_stat(intf, sent_ipmb_responses); format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid, msgid, broadcast, source_address, source_lun); /* * Save the receive message so we can use it * to deliver the response. */ smi_msg->user_data = recv_msg; } else { /* It's a command, so get a sequence for it. */ spin_lock_irqsave(&(intf->seq_lock), flags); ipmi_inc_stat(intf, sent_ipmb_commands); /* * Create a sequence number with a 1 second * timeout and 4 retries. */ rv = intf_next_seq(intf, recv_msg, retry_time_ms, retries, broadcast, &ipmb_seq, &seqid); if (rv) { /* * We have used up all the sequence numbers, * probably, so abort. */ spin_unlock_irqrestore(&(intf->seq_lock), flags); goto out_err; } /* * Store the sequence number in the message, * so that when the send message response * comes back we can start the timer. */ format_ipmb_msg(smi_msg, msg, ipmb_addr, STORE_SEQ_IN_MSGID(ipmb_seq, seqid), ipmb_seq, broadcast, source_address, source_lun); /* * Copy the message into the recv message data, so we * can retransmit it later if necessary. */ memcpy(recv_msg->msg_data, smi_msg->data, smi_msg->data_size); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = smi_msg->data_size; /* * We don't unlock until here, because we need * to copy the completed message into the * recv_msg before we release the lock. * Otherwise, race conditions may bite us. I * know that's pretty paranoid, but I prefer * to be correct. */ spin_unlock_irqrestore(&(intf->seq_lock), flags); } } else if (addr->addr_type == IPMI_LAN_ADDR_TYPE) { struct ipmi_lan_addr *lan_addr; unsigned char ipmb_seq; long seqid; if (addr->channel >= IPMI_MAX_CHANNELS) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } if ((intf->channels[addr->channel].medium != IPMI_CHANNEL_MEDIUM_8023LAN) && (intf->channels[addr->channel].medium != IPMI_CHANNEL_MEDIUM_ASYNC)) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } retries = 4; /* Default to 1 second retries. */ if (retry_time_ms == 0) retry_time_ms = 1000; /* 11 for the header and 1 for the checksum. */ if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } lan_addr = (struct ipmi_lan_addr *) addr; if (lan_addr->lun > 3) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr)); if (recv_msg->msg.netfn & 0x1) { /* * It's a response, so use the user's sequence * from msgid. */ ipmi_inc_stat(intf, sent_lan_responses); format_lan_msg(smi_msg, msg, lan_addr, msgid, msgid, source_lun); /* * Save the receive message so we can use it * to deliver the response. */ smi_msg->user_data = recv_msg; } else { /* It's a command, so get a sequence for it. */ spin_lock_irqsave(&(intf->seq_lock), flags); ipmi_inc_stat(intf, sent_lan_commands); /* * Create a sequence number with a 1 second * timeout and 4 retries. */ rv = intf_next_seq(intf, recv_msg, retry_time_ms, retries, 0, &ipmb_seq, &seqid); if (rv) { /* * We have used up all the sequence numbers, * probably, so abort. */ spin_unlock_irqrestore(&(intf->seq_lock), flags); goto out_err; } /* * Store the sequence number in the message, * so that when the send message response * comes back we can start the timer. */ format_lan_msg(smi_msg, msg, lan_addr, STORE_SEQ_IN_MSGID(ipmb_seq, seqid), ipmb_seq, source_lun); /* * Copy the message into the recv message data, so we * can retransmit it later if necessary. */ memcpy(recv_msg->msg_data, smi_msg->data, smi_msg->data_size); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = smi_msg->data_size; /* * We don't unlock until here, because we need * to copy the completed message into the * recv_msg before we release the lock. * Otherwise, race conditions may bite us. I * know that's pretty paranoid, but I prefer * to be correct. */ spin_unlock_irqrestore(&(intf->seq_lock), flags); } } else { /* Unknown address type. */ ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } #ifdef DEBUG_MSGING { int m; for (m = 0; m < smi_msg->data_size; m++) printk(" %2.2x", smi_msg->data[m]); printk("\n"); } #endif handlers->sender(intf->send_info, smi_msg, priority); rcu_read_unlock(); return 0; out_err: rcu_read_unlock(); ipmi_free_smi_msg(smi_msg); ipmi_free_recv_msg(recv_msg); return rv; } static int check_addr(ipmi_smi_t intf, struct ipmi_addr *addr, unsigned char *saddr, unsigned char *lun) { if (addr->channel >= IPMI_MAX_CHANNELS) return -EINVAL; *lun = intf->channels[addr->channel].lun; *saddr = intf->channels[addr->channel].address; return 0; } int ipmi_request_settime(ipmi_user_t user, struct ipmi_addr *addr, long msgid, struct kernel_ipmi_msg *msg, void *user_msg_data, int priority, int retries, unsigned int retry_time_ms) { unsigned char saddr, lun; int rv; if (!user) return -EINVAL; rv = check_addr(user->intf, addr, &saddr, &lun); if (rv) return rv; return i_ipmi_request(user, user->intf, addr, msgid, msg, user_msg_data, NULL, NULL, priority, saddr, lun, retries, retry_time_ms); } EXPORT_SYMBOL(ipmi_request_settime); int ipmi_request_supply_msgs(ipmi_user_t user, struct ipmi_addr *addr, long msgid, struct kernel_ipmi_msg *msg, void *user_msg_data, void *supplied_smi, struct ipmi_recv_msg *supplied_recv, int priority) { unsigned char saddr, lun; int rv; if (!user) return -EINVAL; rv = check_addr(user->intf, addr, &saddr, &lun); if (rv) return rv; return i_ipmi_request(user, user->intf, addr, msgid, msg, user_msg_data, supplied_smi, supplied_recv, priority, saddr, lun, -1, 0); } EXPORT_SYMBOL(ipmi_request_supply_msgs); #ifdef CONFIG_PROC_FS static int ipmb_file_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = (char *) page; ipmi_smi_t intf = data; int i; int rv = 0; for (i = 0; i < IPMI_MAX_CHANNELS; i++) rv += sprintf(out+rv, "%x ", intf->channels[i].address); out[rv-1] = '\n'; /* Replace the final space with a newline */ out[rv] = '\0'; rv++; return rv; } static int version_file_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = (char *) page; ipmi_smi_t intf = data; return sprintf(out, "%u.%u\n", ipmi_version_major(&intf->bmc->id), ipmi_version_minor(&intf->bmc->id)); } static int stat_file_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = (char *) page; ipmi_smi_t intf = data; out += sprintf(out, "sent_invalid_commands: %u\n", ipmi_get_stat(intf, sent_invalid_commands)); out += sprintf(out, "sent_local_commands: %u\n", ipmi_get_stat(intf, sent_local_commands)); out += sprintf(out, "handled_local_responses: %u\n", ipmi_get_stat(intf, handled_local_responses)); out += sprintf(out, "unhandled_local_responses: %u\n", ipmi_get_stat(intf, unhandled_local_responses)); out += sprintf(out, "sent_ipmb_commands: %u\n", ipmi_get_stat(intf, sent_ipmb_commands)); out += sprintf(out, "sent_ipmb_command_errs: %u\n", ipmi_get_stat(intf, sent_ipmb_command_errs)); out += sprintf(out, "retransmitted_ipmb_commands: %u\n", ipmi_get_stat(intf, retransmitted_ipmb_commands)); out += sprintf(out, "timed_out_ipmb_commands: %u\n", ipmi_get_stat(intf, timed_out_ipmb_commands)); out += sprintf(out, "timed_out_ipmb_broadcasts: %u\n", ipmi_get_stat(intf, timed_out_ipmb_broadcasts)); out += sprintf(out, "sent_ipmb_responses: %u\n", ipmi_get_stat(intf, sent_ipmb_responses)); out += sprintf(out, "handled_ipmb_responses: %u\n", ipmi_get_stat(intf, handled_ipmb_responses)); out += sprintf(out, "invalid_ipmb_responses: %u\n", ipmi_get_stat(intf, invalid_ipmb_responses)); out += sprintf(out, "unhandled_ipmb_responses: %u\n", ipmi_get_stat(intf, unhandled_ipmb_responses)); out += sprintf(out, "sent_lan_commands: %u\n", ipmi_get_stat(intf, sent_lan_commands)); out += sprintf(out, "sent_lan_command_errs: %u\n", ipmi_get_stat(intf, sent_lan_command_errs)); out += sprintf(out, "retransmitted_lan_commands: %u\n", ipmi_get_stat(intf, retransmitted_lan_commands)); out += sprintf(out, "timed_out_lan_commands: %u\n", ipmi_get_stat(intf, timed_out_lan_commands)); out += sprintf(out, "sent_lan_responses: %u\n", ipmi_get_stat(intf, sent_lan_responses)); out += sprintf(out, "handled_lan_responses: %u\n", ipmi_get_stat(intf, handled_lan_responses)); out += sprintf(out, "invalid_lan_responses: %u\n", ipmi_get_stat(intf, invalid_lan_responses)); out += sprintf(out, "unhandled_lan_responses: %u\n", ipmi_get_stat(intf, unhandled_lan_responses)); out += sprintf(out, "handled_commands: %u\n", ipmi_get_stat(intf, handled_commands)); out += sprintf(out, "invalid_commands: %u\n", ipmi_get_stat(intf, invalid_commands)); out += sprintf(out, "unhandled_commands: %u\n", ipmi_get_stat(intf, unhandled_commands)); out += sprintf(out, "invalid_events: %u\n", ipmi_get_stat(intf, invalid_events)); out += sprintf(out, "events: %u\n", ipmi_get_stat(intf, events)); return (out - ((char *) page)); } #endif /* CONFIG_PROC_FS */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, read_proc_t *read_proc, write_proc_t *write_proc, void *data, struct module *owner) { int rv = 0; #ifdef CONFIG_PROC_FS struct proc_dir_entry *file; struct ipmi_proc_entry *entry; /* Create a list element. */ entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; entry->name = kmalloc(strlen(name)+1, GFP_KERNEL); if (!entry->name) { kfree(entry); return -ENOMEM; } strcpy(entry->name, name); file = create_proc_entry(name, 0, smi->proc_dir); if (!file) { kfree(entry->name); kfree(entry); rv = -ENOMEM; } else { file->data = data; file->read_proc = read_proc; file->write_proc = write_proc; file->owner = owner; mutex_lock(&smi->proc_entry_lock); /* Stick it on the list. */ entry->next = smi->proc_entries; smi->proc_entries = entry; mutex_unlock(&smi->proc_entry_lock); } #endif /* CONFIG_PROC_FS */ return rv; } EXPORT_SYMBOL(ipmi_smi_add_proc_entry); static int add_proc_entries(ipmi_smi_t smi, int num) { int rv = 0; #ifdef CONFIG_PROC_FS sprintf(smi->proc_dir_name, "%d", num); smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) rv = -ENOMEM; else smi->proc_dir->owner = THIS_MODULE; if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "stats", stat_file_read_proc, NULL, smi, THIS_MODULE); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "ipmb", ipmb_file_read_proc, NULL, smi, THIS_MODULE); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "version", version_file_read_proc, NULL, smi, THIS_MODULE); #endif /* CONFIG_PROC_FS */ return rv; } static void remove_proc_entries(ipmi_smi_t smi) { #ifdef CONFIG_PROC_FS struct ipmi_proc_entry *entry; mutex_lock(&smi->proc_entry_lock); while (smi->proc_entries) { entry = smi->proc_entries; smi->proc_entries = entry->next; remove_proc_entry(entry->name, smi->proc_dir); kfree(entry->name); kfree(entry); } mutex_unlock(&smi->proc_entry_lock); remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); #endif /* CONFIG_PROC_FS */ } static int __find_bmc_guid(struct device *dev, void *data) { unsigned char *id = data; struct bmc_device *bmc = dev_get_drvdata(dev); return memcmp(bmc->guid, id, 16) == 0; } static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv, unsigned char *guid) { struct device *dev; dev = driver_find_device(drv, NULL, guid, __find_bmc_guid); if (dev) return dev_get_drvdata(dev); else return NULL; } struct prod_dev_id { unsigned int product_id; unsigned char device_id; }; static int __find_bmc_prod_dev_id(struct device *dev, void *data) { struct prod_dev_id *id = data; struct bmc_device *bmc = dev_get_drvdata(dev); return (bmc->id.product_id == id->product_id && bmc->id.device_id == id->device_id); } static struct bmc_device *ipmi_find_bmc_prod_dev_id( struct device_driver *drv, unsigned int product_id, unsigned char device_id) { struct prod_dev_id id = { .product_id = product_id, .device_id = device_id, }; struct device *dev; dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id); if (dev) return dev_get_drvdata(dev); else return NULL; } static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 10, "%u\n", bmc->id.device_id); } static ssize_t provides_dev_sdrs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 10, "%u\n", (bmc->id.device_revision & 0x80) >> 7); } static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 20, "%u\n", bmc->id.device_revision & 0x0F); } static ssize_t firmware_rev_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1, bmc->id.firmware_revision_2); } static ssize_t ipmi_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 20, "%u.%u\n", ipmi_version_major(&bmc->id), ipmi_version_minor(&bmc->id)); } static ssize_t add_dev_support_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 10, "0x%02x\n", bmc->id.additional_device_support); } static ssize_t manufacturer_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id); } static ssize_t product_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id); } static ssize_t aux_firmware_rev_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n", bmc->id.aux_firmware_revision[3], bmc->id.aux_firmware_revision[2], bmc->id.aux_firmware_revision[1], bmc->id.aux_firmware_revision[0]); } static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 100, "%Lx%Lx\n", (long long) bmc->guid[0], (long long) bmc->guid[8]); } static void remove_files(struct bmc_device *bmc) { if (!bmc->dev) return; device_remove_file(&bmc->dev->dev, &bmc->device_id_attr); device_remove_file(&bmc->dev->dev, &bmc->provides_dev_sdrs_attr); device_remove_file(&bmc->dev->dev, &bmc->revision_attr); device_remove_file(&bmc->dev->dev, &bmc->firmware_rev_attr); device_remove_file(&bmc->dev->dev, &bmc->version_attr); device_remove_file(&bmc->dev->dev, &bmc->add_dev_support_attr); device_remove_file(&bmc->dev->dev, &bmc->manufacturer_id_attr); device_remove_file(&bmc->dev->dev, &bmc->product_id_attr); if (bmc->id.aux_firmware_revision_set) device_remove_file(&bmc->dev->dev, &bmc->aux_firmware_rev_attr); if (bmc->guid_set) device_remove_file(&bmc->dev->dev, &bmc->guid_attr); } static void cleanup_bmc_device(struct kref *ref) { struct bmc_device *bmc; bmc = container_of(ref, struct bmc_device, refcount); remove_files(bmc); platform_device_unregister(bmc->dev); kfree(bmc); } static void ipmi_bmc_unregister(ipmi_smi_t intf) { struct bmc_device *bmc = intf->bmc; if (intf->sysfs_name) { sysfs_remove_link(&intf->si_dev->kobj, intf->sysfs_name); kfree(intf->sysfs_name); intf->sysfs_name = NULL; } if (intf->my_dev_name) { sysfs_remove_link(&bmc->dev->dev.kobj, intf->my_dev_name); kfree(intf->my_dev_name); intf->my_dev_name = NULL; } mutex_lock(&ipmidriver_mutex); kref_put(&bmc->refcount, cleanup_bmc_device); intf->bmc = NULL; mutex_unlock(&ipmidriver_mutex); } static int create_files(struct bmc_device *bmc) { int err; bmc->device_id_attr.attr.name = "device_id"; bmc->device_id_attr.attr.mode = S_IRUGO; bmc->device_id_attr.show = device_id_show; bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs"; bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO; bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show; bmc->revision_attr.attr.name = "revision"; bmc->revision_attr.attr.mode = S_IRUGO; bmc->revision_attr.show = revision_show; bmc->firmware_rev_attr.attr.name = "firmware_revision"; bmc->firmware_rev_attr.attr.mode = S_IRUGO; bmc->firmware_rev_attr.show = firmware_rev_show; bmc->version_attr.attr.name = "ipmi_version"; bmc->version_attr.attr.mode = S_IRUGO; bmc->version_attr.show = ipmi_version_show; bmc->add_dev_support_attr.attr.name = "additional_device_support"; bmc->add_dev_support_attr.attr.mode = S_IRUGO; bmc->add_dev_support_attr.show = add_dev_support_show; bmc->manufacturer_id_attr.attr.name = "manufacturer_id"; bmc->manufacturer_id_attr.attr.mode = S_IRUGO; bmc->manufacturer_id_attr.show = manufacturer_id_show; bmc->product_id_attr.attr.name = "product_id"; bmc->product_id_attr.attr.mode = S_IRUGO; bmc->product_id_attr.show = product_id_show; bmc->guid_attr.attr.name = "guid"; bmc->guid_attr.attr.mode = S_IRUGO; bmc->guid_attr.show = guid_show; bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO; bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show; err = device_create_file(&bmc->dev->dev, &bmc->device_id_attr); if (err) goto out; err = device_create_file(&bmc->dev->dev, &bmc->provides_dev_sdrs_attr); if (err) goto out_devid; err = device_create_file(&bmc->dev->dev, &bmc->revision_attr); if (err) goto out_sdrs; err = device_create_file(&bmc->dev->dev, &bmc->firmware_rev_attr); if (err) goto out_rev; err = device_create_file(&bmc->dev->dev, &bmc->version_attr); if (err) goto out_firm; err = device_create_file(&bmc->dev->dev, &bmc->add_dev_support_attr); if (err) goto out_version; err = device_create_file(&bmc->dev->dev, &bmc->manufacturer_id_attr); if (err) goto out_add_dev; err = device_create_file(&bmc->dev->dev, &bmc->product_id_attr); if (err) goto out_manu; if (bmc->id.aux_firmware_revision_set) { err = device_create_file(&bmc->dev->dev, &bmc->aux_firmware_rev_attr); if (err) goto out_prod_id; } if (bmc->guid_set) { err = device_create_file(&bmc->dev->dev, &bmc->guid_attr); if (err) goto out_aux_firm; } return 0; out_aux_firm: if (bmc->id.aux_firmware_revision_set) device_remove_file(&bmc->dev->dev, &bmc->aux_firmware_rev_attr); out_prod_id: device_remove_file(&bmc->dev->dev, &bmc->product_id_attr); out_manu: device_remove_file(&bmc->dev->dev, &bmc->manufacturer_id_attr); out_add_dev: device_remove_file(&bmc->dev->dev, &bmc->add_dev_support_attr); out_version: device_remove_file(&bmc->dev->dev, &bmc->version_attr); out_firm: device_remove_file(&bmc->dev->dev, &bmc->firmware_rev_attr); out_rev: device_remove_file(&bmc->dev->dev, &bmc->revision_attr); out_sdrs: device_remove_file(&bmc->dev->dev, &bmc->provides_dev_sdrs_attr); out_devid: device_remove_file(&bmc->dev->dev, &bmc->device_id_attr); out: return err; } static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, const char *sysfs_name) { int rv; struct bmc_device *bmc = intf->bmc; struct bmc_device *old_bmc; int size; char dummy[1]; mutex_lock(&ipmidriver_mutex); /* * Try to find if there is an bmc_device struct * representing the interfaced BMC already */ if (bmc->guid_set) old_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid); else old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver, bmc->id.product_id, bmc->id.device_id); /* * If there is already an bmc_device, free the new one, * otherwise register the new BMC device */ if (old_bmc) { kfree(bmc); intf->bmc = old_bmc; bmc = old_bmc; kref_get(&bmc->refcount); mutex_unlock(&ipmidriver_mutex); printk(KERN_INFO "ipmi: interfacing existing BMC (man_id: 0x%6.6x," " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", bmc->id.manufacturer_id, bmc->id.product_id, bmc->id.device_id); } else { char name[14]; unsigned char orig_dev_id = bmc->id.device_id; int warn_printed = 0; snprintf(name, sizeof(name), "ipmi_bmc.%4.4x", bmc->id.product_id); while (ipmi_find_bmc_prod_dev_id(&ipmidriver, bmc->id.product_id, bmc->id.device_id)) { if (!warn_printed) { printk(KERN_WARNING PFX "This machine has two different BMCs" " with the same product id and device" " id. This is an error in the" " firmware, but incrementing the" " device id to work around the problem." " Prod ID = 0x%x, Dev ID = 0x%x\n", bmc->id.product_id, bmc->id.device_id); warn_printed = 1; } bmc->id.device_id++; /* Wraps at 255 */ if (bmc->id.device_id == orig_dev_id) { printk(KERN_ERR PFX "Out of device ids!\n"); break; } } bmc->dev = platform_device_alloc(name, bmc->id.device_id); if (!bmc->dev) { mutex_unlock(&ipmidriver_mutex); printk(KERN_ERR "ipmi_msghandler:" " Unable to allocate platform device\n"); return -ENOMEM; } bmc->dev->dev.driver = &ipmidriver; dev_set_drvdata(&bmc->dev->dev, bmc); kref_init(&bmc->refcount); rv = platform_device_add(bmc->dev); mutex_unlock(&ipmidriver_mutex); if (rv) { platform_device_put(bmc->dev); bmc->dev = NULL; printk(KERN_ERR "ipmi_msghandler:" " Unable to register bmc device: %d\n", rv); /* * Don't go to out_err, you can only do that if * the device is registered already. */ return rv; } rv = create_files(bmc); if (rv) { mutex_lock(&ipmidriver_mutex); platform_device_unregister(bmc->dev); mutex_unlock(&ipmidriver_mutex); return rv; } printk(KERN_INFO "ipmi: Found new BMC (man_id: 0x%6.6x, " " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", bmc->id.manufacturer_id, bmc->id.product_id, bmc->id.device_id); } /* * create symlink from system interface device to bmc device * and back. */ intf->sysfs_name = kstrdup(sysfs_name, GFP_KERNEL); if (!intf->sysfs_name) { rv = -ENOMEM; printk(KERN_ERR "ipmi_msghandler: allocate link to BMC: %d\n", rv); goto out_err; } rv = sysfs_create_link(&intf->si_dev->kobj, &bmc->dev->dev.kobj, intf->sysfs_name); if (rv) { kfree(intf->sysfs_name); intf->sysfs_name = NULL; printk(KERN_ERR "ipmi_msghandler: Unable to create bmc symlink: %d\n", rv); goto out_err; } size = snprintf(dummy, 0, "ipmi%d", ifnum); intf->my_dev_name = kmalloc(size+1, GFP_KERNEL); if (!intf->my_dev_name) { kfree(intf->sysfs_name); intf->sysfs_name = NULL; rv = -ENOMEM; printk(KERN_ERR "ipmi_msghandler: allocate link from BMC: %d\n", rv); goto out_err; } snprintf(intf->my_dev_name, size+1, "ipmi%d", ifnum); rv = sysfs_create_link(&bmc->dev->dev.kobj, &intf->si_dev->kobj, intf->my_dev_name); if (rv) { kfree(intf->sysfs_name); intf->sysfs_name = NULL; kfree(intf->my_dev_name); intf->my_dev_name = NULL; printk(KERN_ERR "ipmi_msghandler:" " Unable to create symlink to bmc: %d\n", rv); goto out_err; } return 0; out_err: ipmi_bmc_unregister(intf); return rv; } static int send_guid_cmd(ipmi_smi_t intf, int chan) { struct kernel_ipmi_msg msg; struct ipmi_system_interface_addr si; si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si.channel = IPMI_BMC_CHANNEL; si.lun = 0; msg.netfn = IPMI_NETFN_APP_REQUEST; msg.cmd = IPMI_GET_DEVICE_GUID_CMD; msg.data = NULL; msg.data_len = 0; return i_ipmi_request(NULL, intf, (struct ipmi_addr *) &si, 0, &msg, intf, NULL, NULL, 0, intf->channels[0].address, intf->channels[0].lun, -1, 0); } static void guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) { if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD)) /* Not for me */ return; if (msg->msg.data[0] != 0) { /* Error from getting the GUID, the BMC doesn't have one. */ intf->bmc->guid_set = 0; goto out; } if (msg->msg.data_len < 17) { intf->bmc->guid_set = 0; printk(KERN_WARNING PFX "guid_handler: The GUID response from the BMC was too" " short, it was %d but should have been 17. Assuming" " GUID is not available.\n", msg->msg.data_len); goto out; } memcpy(intf->bmc->guid, msg->msg.data, 16); intf->bmc->guid_set = 1; out: wake_up(&intf->waitq); } static void get_guid(ipmi_smi_t intf) { int rv; intf->bmc->guid_set = 0x2; intf->null_user_handler = guid_handler; rv = send_guid_cmd(intf, 0); if (rv) /* Send failed, no GUID available. */ intf->bmc->guid_set = 0; wait_event(intf->waitq, intf->bmc->guid_set != 2); intf->null_user_handler = NULL; } static int send_channel_info_cmd(ipmi_smi_t intf, int chan) { struct kernel_ipmi_msg msg; unsigned char data[1]; struct ipmi_system_interface_addr si; si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si.channel = IPMI_BMC_CHANNEL; si.lun = 0; msg.netfn = IPMI_NETFN_APP_REQUEST; msg.cmd = IPMI_GET_CHANNEL_INFO_CMD; msg.data = data; msg.data_len = 1; data[0] = chan; return i_ipmi_request(NULL, intf, (struct ipmi_addr *) &si, 0, &msg, intf, NULL, NULL, 0, intf->channels[0].address, intf->channels[0].lun, -1, 0); } static void channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) { int rv = 0; int chan; if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD)) { /* It's the one we want */ if (msg->msg.data[0] != 0) { /* Got an error from the channel, just go on. */ if (msg->msg.data[0] == IPMI_INVALID_COMMAND_ERR) { /* * If the MC does not support this * command, that is legal. We just * assume it has one IPMB at channel * zero. */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; rv = -ENOSYS; intf->curr_channel = IPMI_MAX_CHANNELS; wake_up(&intf->waitq); goto out; } goto next_channel; } if (msg->msg.data_len < 4) { /* Message not big enough, just go on. */ goto next_channel; } chan = intf->curr_channel; intf->channels[chan].medium = msg->msg.data[2] & 0x7f; intf->channels[chan].protocol = msg->msg.data[3] & 0x1f; next_channel: intf->curr_channel++; if (intf->curr_channel >= IPMI_MAX_CHANNELS) wake_up(&intf->waitq); else rv = send_channel_info_cmd(intf, intf->curr_channel); if (rv) { /* Got an error somehow, just give up. */ intf->curr_channel = IPMI_MAX_CHANNELS; wake_up(&intf->waitq); printk(KERN_WARNING PFX "Error sending channel information: %d\n", rv); } } out: return; } void ipmi_poll_interface(ipmi_user_t user) { ipmi_smi_t intf = user->intf; if (intf->handlers->poll) intf->handlers->poll(intf->send_info); } EXPORT_SYMBOL(ipmi_poll_interface); int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, struct ipmi_device_id *device_id, struct device *si_dev, const char *sysfs_name, unsigned char slave_addr) { int i, j; int rv; ipmi_smi_t intf; ipmi_smi_t tintf; struct list_head *link; /* * Make sure the driver is actually initialized, this handles * problems with initialization order. */ if (!initialized) { rv = ipmi_init_msghandler(); if (rv) return rv; /* * The init code doesn't return an error if it was turned * off, but it won't initialize. Check that. */ if (!initialized) return -ENODEV; } intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) return -ENOMEM; intf->ipmi_version_major = ipmi_version_major(device_id); intf->ipmi_version_minor = ipmi_version_minor(device_id); intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL); if (!intf->bmc) { kfree(intf); return -ENOMEM; } intf->intf_num = -1; /* Mark it invalid for now. */ kref_init(&intf->refcount); intf->bmc->id = *device_id; intf->si_dev = si_dev; for (j = 0; j < IPMI_MAX_CHANNELS; j++) { intf->channels[j].address = IPMI_BMC_SLAVE_ADDR; intf->channels[j].lun = 2; } if (slave_addr != 0) intf->channels[0].address = slave_addr; INIT_LIST_HEAD(&intf->users); intf->handlers = handlers; intf->send_info = send_info; spin_lock_init(&intf->seq_lock); for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) { intf->seq_table[j].inuse = 0; intf->seq_table[j].seqid = 0; } intf->curr_seq = 0; #ifdef CONFIG_PROC_FS mutex_init(&intf->proc_entry_lock); #endif spin_lock_init(&intf->waiting_msgs_lock); INIT_LIST_HEAD(&intf->waiting_msgs); spin_lock_init(&intf->events_lock); INIT_LIST_HEAD(&intf->waiting_events); intf->waiting_events_count = 0; mutex_init(&intf->cmd_rcvrs_mutex); spin_lock_init(&intf->maintenance_mode_lock); INIT_LIST_HEAD(&intf->cmd_rcvrs); init_waitqueue_head(&intf->waitq); for (i = 0; i < IPMI_NUM_STATS; i++) atomic_set(&intf->stats[i], 0); intf->proc_dir = NULL; mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); /* Look for a hole in the numbers. */ i = 0; link = &ipmi_interfaces; list_for_each_entry_rcu(tintf, &ipmi_interfaces, link) { if (tintf->intf_num != i) { link = &tintf->link; break; } i++; } /* Add the new interface in numeric order. */ if (i == 0) list_add_rcu(&intf->link, &ipmi_interfaces); else list_add_tail_rcu(&intf->link, link); rv = handlers->start_processing(send_info, intf); if (rv) goto out; get_guid(intf); if ((intf->ipmi_version_major > 1) || ((intf->ipmi_version_major == 1) && (intf->ipmi_version_minor >= 5))) { /* * Start scanning the channels to see what is * available. */ intf->null_user_handler = channel_handler; intf->curr_channel = 0; rv = send_channel_info_cmd(intf, 0); if (rv) goto out; /* Wait for the channel info to be read. */ wait_event(intf->waitq, intf->curr_channel >= IPMI_MAX_CHANNELS); intf->null_user_handler = NULL; } else { /* Assume a single IPMB channel at zero. */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; } if (rv == 0) rv = add_proc_entries(intf, i); rv = ipmi_bmc_register(intf, i, sysfs_name); out: if (rv) { if (intf->proc_dir) remove_proc_entries(intf); intf->handlers = NULL; list_del_rcu(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); mutex_unlock(&smi_watchers_mutex); synchronize_rcu(); kref_put(&intf->refcount, intf_free); } else { /* * Keep memory order straight for RCU readers. Make * sure everything else is committed to memory before * setting intf_num to mark the interface valid. */ smp_wmb(); intf->intf_num = i; mutex_unlock(&ipmi_interfaces_mutex); /* After this point the interface is legal to use. */ call_smi_watchers(i, intf->si_dev); mutex_unlock(&smi_watchers_mutex); } return rv; } EXPORT_SYMBOL(ipmi_register_smi); static void cleanup_smi_msgs(ipmi_smi_t intf) { int i; struct seq_table *ent; /* No need for locks, the interface is down. */ for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { ent = &(intf->seq_table[i]); if (!ent->inuse) continue; deliver_err_response(ent->recv_msg, IPMI_ERR_UNSPECIFIED); } } int ipmi_unregister_smi(ipmi_smi_t intf) { struct ipmi_smi_watcher *w; int intf_num = intf->intf_num; ipmi_bmc_unregister(intf); mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); intf->intf_num = -1; intf->handlers = NULL; list_del_rcu(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); synchronize_rcu(); cleanup_smi_msgs(intf); remove_proc_entries(intf); /* * Call all the watcher interfaces to tell them that * an interface is gone. */ list_for_each_entry(w, &smi_watchers, link) w->smi_gone(intf_num); mutex_unlock(&smi_watchers_mutex); kref_put(&intf->refcount, intf_free); return 0; } EXPORT_SYMBOL(ipmi_unregister_smi); static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct ipmi_ipmb_addr ipmb_addr; struct ipmi_recv_msg *recv_msg; /* * This is 11, not 10, because the response must contain a * completion code. */ if (msg->rsp_size < 11) { /* Message not big enough, just ignore it. */ ipmi_inc_stat(intf, invalid_ipmb_responses); return 0; } if (msg->rsp[2] != 0) { /* An error getting the response, just ignore it. */ return 0; } ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; ipmb_addr.slave_addr = msg->rsp[6]; ipmb_addr.channel = msg->rsp[3] & 0x0f; ipmb_addr.lun = msg->rsp[7] & 3; /* * It's a response from a remote entity. Look up the sequence * number and handle the response. */ if (intf_find_seq(intf, msg->rsp[7] >> 2, msg->rsp[3] & 0x0f, msg->rsp[8], (msg->rsp[4] >> 2) & (~1), (struct ipmi_addr *) &(ipmb_addr), &recv_msg)) { /* * We were unable to find the sequence number, * so just nuke the message. */ ipmi_inc_stat(intf, unhandled_ipmb_responses); return 0; } memcpy(recv_msg->msg_data, &(msg->rsp[9]), msg->rsp_size - 9); /* * The other fields matched, so no need to set them, except * for netfn, which needs to be the response that was * returned, not the request value. */ recv_msg->msg.netfn = msg->rsp[4] >> 2; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 10; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; ipmi_inc_stat(intf, handled_ipmb_responses); deliver_response(recv_msg); return 0; } static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct cmd_rcvr *rcvr; int rv = 0; unsigned char netfn; unsigned char cmd; unsigned char chan; ipmi_user_t user = NULL; struct ipmi_ipmb_addr *ipmb_addr; struct ipmi_recv_msg *recv_msg; struct ipmi_smi_handlers *handlers; if (msg->rsp_size < 10) { /* Message not big enough, just ignore it. */ ipmi_inc_stat(intf, invalid_commands); return 0; } if (msg->rsp[2] != 0) { /* An error getting the response, just ignore it. */ return 0; } netfn = msg->rsp[4] >> 2; cmd = msg->rsp[8]; chan = msg->rsp[3] & 0xf; rcu_read_lock(); rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; kref_get(&user->refcount); } else user = NULL; rcu_read_unlock(); if (user == NULL) { /* We didn't find a user, deliver an error response. */ ipmi_inc_stat(intf, unhandled_commands); msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); msg->data[1] = IPMI_SEND_MSG_CMD; msg->data[2] = msg->rsp[3]; msg->data[3] = msg->rsp[6]; msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); msg->data[5] = ipmb_checksum(&(msg->data[3]), 2); msg->data[6] = intf->channels[msg->rsp[3] & 0xf].address; /* rqseq/lun */ msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); msg->data[8] = msg->rsp[8]; /* cmd */ msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE; msg->data[10] = ipmb_checksum(&(msg->data[6]), 4); msg->data_size = 11; #ifdef DEBUG_MSGING { int m; printk("Invalid command:"); for (m = 0; m < msg->data_size; m++) printk(" %2.2x", msg->data[m]); printk("\n"); } #endif rcu_read_lock(); handlers = intf->handlers; if (handlers) { handlers->sender(intf->send_info, msg, 0); /* * We used the message, so return the value * that causes it to not be freed or * queued. */ rv = -1; } rcu_read_unlock(); } else { /* Deliver the message to the user. */ ipmi_inc_stat(intf, handled_commands); recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { /* * We couldn't allocate memory for the * message, so requeue it for handling * later. */ rv = 1; kref_put(&user->refcount, free_user); } else { /* Extract the source address from the data. */ ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr; ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE; ipmb_addr->slave_addr = msg->rsp[6]; ipmb_addr->lun = msg->rsp[7] & 3; ipmb_addr->channel = msg->rsp[3] & 0xf; /* * Extract the rest of the message information * from the IPMB header. */ recv_msg->user = user; recv_msg->recv_type = IPMI_CMD_RECV_TYPE; recv_msg->msgid = msg->rsp[7] >> 2; recv_msg->msg.netfn = msg->rsp[4] >> 2; recv_msg->msg.cmd = msg->rsp[8]; recv_msg->msg.data = recv_msg->msg_data; /* * We chop off 10, not 9 bytes because the checksum * at the end also needs to be removed. */ recv_msg->msg.data_len = msg->rsp_size - 10; memcpy(recv_msg->msg_data, &(msg->rsp[9]), msg->rsp_size - 10); deliver_response(recv_msg); } } return rv; } static int handle_lan_get_msg_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct ipmi_lan_addr lan_addr; struct ipmi_recv_msg *recv_msg; /* * This is 13, not 12, because the response must contain a * completion code. */ if (msg->rsp_size < 13) { /* Message not big enough, just ignore it. */ ipmi_inc_stat(intf, invalid_lan_responses); return 0; } if (msg->rsp[2] != 0) { /* An error getting the response, just ignore it. */ return 0; } lan_addr.addr_type = IPMI_LAN_ADDR_TYPE; lan_addr.session_handle = msg->rsp[4]; lan_addr.remote_SWID = msg->rsp[8]; lan_addr.local_SWID = msg->rsp[5]; lan_addr.channel = msg->rsp[3] & 0x0f; lan_addr.privilege = msg->rsp[3] >> 4; lan_addr.lun = msg->rsp[9] & 3; /* * It's a response from a remote entity. Look up the sequence * number and handle the response. */ if (intf_find_seq(intf, msg->rsp[9] >> 2, msg->rsp[3] & 0x0f, msg->rsp[10], (msg->rsp[6] >> 2) & (~1), (struct ipmi_addr *) &(lan_addr), &recv_msg)) { /* * We were unable to find the sequence number, * so just nuke the message. */ ipmi_inc_stat(intf, unhandled_lan_responses); return 0; } memcpy(recv_msg->msg_data, &(msg->rsp[11]), msg->rsp_size - 11); /* * The other fields matched, so no need to set them, except * for netfn, which needs to be the response that was * returned, not the request value. */ recv_msg->msg.netfn = msg->rsp[6] >> 2; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 12; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; ipmi_inc_stat(intf, handled_lan_responses); deliver_response(recv_msg); return 0; } static int handle_lan_get_msg_cmd(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct cmd_rcvr *rcvr; int rv = 0; unsigned char netfn; unsigned char cmd; unsigned char chan; ipmi_user_t user = NULL; struct ipmi_lan_addr *lan_addr; struct ipmi_recv_msg *recv_msg; if (msg->rsp_size < 12) { /* Message not big enough, just ignore it. */ ipmi_inc_stat(intf, invalid_commands); return 0; } if (msg->rsp[2] != 0) { /* An error getting the response, just ignore it. */ return 0; } netfn = msg->rsp[6] >> 2; cmd = msg->rsp[10]; chan = msg->rsp[3] & 0xf; rcu_read_lock(); rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; kref_get(&user->refcount); } else user = NULL; rcu_read_unlock(); if (user == NULL) { /* We didn't find a user, just give up. */ ipmi_inc_stat(intf, unhandled_commands); /* * Don't do anything with these messages, just allow * them to be freed. */ rv = 0; } else { /* Deliver the message to the user. */ ipmi_inc_stat(intf, handled_commands); recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { /* * We couldn't allocate memory for the * message, so requeue it for handling later. */ rv = 1; kref_put(&user->refcount, free_user); } else { /* Extract the source address from the data. */ lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr; lan_addr->addr_type = IPMI_LAN_ADDR_TYPE; lan_addr->session_handle = msg->rsp[4]; lan_addr->remote_SWID = msg->rsp[8]; lan_addr->local_SWID = msg->rsp[5]; lan_addr->lun = msg->rsp[9] & 3; lan_addr->channel = msg->rsp[3] & 0xf; lan_addr->privilege = msg->rsp[3] >> 4; /* * Extract the rest of the message information * from the IPMB header. */ recv_msg->user = user; recv_msg->recv_type = IPMI_CMD_RECV_TYPE; recv_msg->msgid = msg->rsp[9] >> 2; recv_msg->msg.netfn = msg->rsp[6] >> 2; recv_msg->msg.cmd = msg->rsp[10]; recv_msg->msg.data = recv_msg->msg_data; /* * We chop off 12, not 11 bytes because the checksum * at the end also needs to be removed. */ recv_msg->msg.data_len = msg->rsp_size - 12; memcpy(recv_msg->msg_data, &(msg->rsp[11]), msg->rsp_size - 12); deliver_response(recv_msg); } } return rv; } static void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg, struct ipmi_smi_msg *msg) { struct ipmi_system_interface_addr *smi_addr; recv_msg->msgid = 0; smi_addr = (struct ipmi_system_interface_addr *) &(recv_msg->addr); smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; smi_addr->channel = IPMI_BMC_CHANNEL; smi_addr->lun = msg->rsp[0] & 3; recv_msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE; recv_msg->msg.netfn = msg->rsp[0] >> 2; recv_msg->msg.cmd = msg->rsp[1]; memcpy(recv_msg->msg_data, &(msg->rsp[3]), msg->rsp_size - 3); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 3; } static int handle_read_event_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct ipmi_recv_msg *recv_msg, *recv_msg2; struct list_head msgs; ipmi_user_t user; int rv = 0; int deliver_count = 0; unsigned long flags; if (msg->rsp_size < 19) { /* Message is too small to be an IPMB event. */ ipmi_inc_stat(intf, invalid_events); return 0; } if (msg->rsp[2] != 0) { /* An error getting the event, just ignore it. */ return 0; } INIT_LIST_HEAD(&msgs); spin_lock_irqsave(&intf->events_lock, flags); ipmi_inc_stat(intf, events); /* * Allocate and fill in one message for every user that is * getting events. */ rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { if (!user->gets_events) continue; recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { rcu_read_unlock(); list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); } /* * We couldn't allocate memory for the * message, so requeue it for handling * later. */ rv = 1; goto out; } deliver_count++; copy_event_into_recv_msg(recv_msg, msg); recv_msg->user = user; kref_get(&user->refcount); list_add_tail(&(recv_msg->link), &msgs); } rcu_read_unlock(); if (deliver_count) { /* Now deliver all the messages. */ list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { list_del(&recv_msg->link); deliver_response(recv_msg); } } else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) { /* * No one to receive the message, put it in queue if there's * not already too many things in the queue. */ recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { /* * We couldn't allocate memory for the * message, so requeue it for handling * later. */ rv = 1; goto out; } copy_event_into_recv_msg(recv_msg, msg); list_add_tail(&(recv_msg->link), &(intf->waiting_events)); intf->waiting_events_count++; } else if (!intf->event_msg_printed) { /* * There's too many things in the queue, discard this * message. */ printk(KERN_WARNING PFX "Event queue full, discarding" " incoming events\n"); intf->event_msg_printed = 1; } out: spin_unlock_irqrestore(&(intf->events_lock), flags); return rv; } static int handle_bmc_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct ipmi_recv_msg *recv_msg; struct ipmi_user *user; recv_msg = (struct ipmi_recv_msg *) msg->user_data; if (recv_msg == NULL) { printk(KERN_WARNING "IPMI message received with no owner. This\n" "could be because of a malformed message, or\n" "because of a hardware error. Contact your\n" "hardware vender for assistance\n"); return 0; } user = recv_msg->user; /* Make sure the user still exists. */ if (user && !user->valid) { /* The user for the message went away, so give up. */ ipmi_inc_stat(intf, unhandled_local_responses); ipmi_free_recv_msg(recv_msg); } else { struct ipmi_system_interface_addr *smi_addr; ipmi_inc_stat(intf, handled_local_responses); recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; recv_msg->msgid = msg->msgid; smi_addr = ((struct ipmi_system_interface_addr *) &(recv_msg->addr)); smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; smi_addr->channel = IPMI_BMC_CHANNEL; smi_addr->lun = msg->rsp[0] & 3; recv_msg->msg.netfn = msg->rsp[0] >> 2; recv_msg->msg.cmd = msg->rsp[1]; memcpy(recv_msg->msg_data, &(msg->rsp[2]), msg->rsp_size - 2); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 2; deliver_response(recv_msg); } return 0; } /* * Handle a new message. Return 1 if the message should be requeued, * 0 if the message should be freed, or -1 if the message should not * be freed or requeued. */ static int handle_new_recv_msg(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { int requeue; int chan; #ifdef DEBUG_MSGING int m; printk("Recv:"); for (m = 0; m < msg->rsp_size; m++) printk(" %2.2x", msg->rsp[m]); printk("\n"); #endif if (msg->rsp_size < 2) { /* Message is too small to be correct. */ printk(KERN_WARNING PFX "BMC returned to small a message" " for netfn %x cmd %x, got %d bytes\n", (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size); /* Generate an error response for the message. */ msg->rsp[0] = msg->data[0] | (1 << 2); msg->rsp[1] = msg->data[1]; msg->rsp[2] = IPMI_ERR_UNSPECIFIED; msg->rsp_size = 3; } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1)) || (msg->rsp[1] != msg->data[1])) { /* * The NetFN and Command in the response is not even * marginally correct. */ printk(KERN_WARNING PFX "BMC returned incorrect response," " expected netfn %x cmd %x, got netfn %x cmd %x\n", (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp[0] >> 2, msg->rsp[1]); /* Generate an error response for the message. */ msg->rsp[0] = msg->data[0] | (1 << 2); msg->rsp[1] = msg->data[1]; msg->rsp[2] = IPMI_ERR_UNSPECIFIED; msg->rsp_size = 3; } if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_SEND_MSG_CMD) && (msg->user_data != NULL)) { /* * It's a response to a response we sent. For this we * deliver a send message response to the user. */ struct ipmi_recv_msg *recv_msg = msg->user_data; requeue = 0; if (msg->rsp_size < 2) /* Message is too small to be correct. */ goto out; chan = msg->data[2] & 0x0f; if (chan >= IPMI_MAX_CHANNELS) /* Invalid channel number */ goto out; if (!recv_msg) goto out; /* Make sure the user still exists. */ if (!recv_msg->user || !recv_msg->user->valid) goto out; recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = 1; recv_msg->msg_data[0] = msg->rsp[2]; deliver_response(recv_msg); } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { /* It's from the receive queue. */ chan = msg->rsp[3] & 0xf; if (chan >= IPMI_MAX_CHANNELS) { /* Invalid channel number */ requeue = 0; goto out; } switch (intf->channels[chan].medium) { case IPMI_CHANNEL_MEDIUM_IPMB: if (msg->rsp[4] & 0x04) { /* * It's a response, so find the * requesting message and send it up. */ requeue = handle_ipmb_get_msg_rsp(intf, msg); } else { /* * It's a command to the SMS from some other * entity. Handle that. */ requeue = handle_ipmb_get_msg_cmd(intf, msg); } break; case IPMI_CHANNEL_MEDIUM_8023LAN: case IPMI_CHANNEL_MEDIUM_ASYNC: if (msg->rsp[6] & 0x04) { /* * It's a response, so find the * requesting message and send it up. */ requeue = handle_lan_get_msg_rsp(intf, msg); } else { /* * It's a command to the SMS from some other * entity. Handle that. */ requeue = handle_lan_get_msg_cmd(intf, msg); } break; default: /* * We don't handle the channel type, so just * free the message. */ requeue = 0; } } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { /* It's an asyncronous event. */ requeue = handle_read_event_rsp(intf, msg); } else { /* It's a response from the local BMC. */ requeue = handle_bmc_rsp(intf, msg); } out: return requeue; } /* Handle a new message from the lower layer. */ void ipmi_smi_msg_received(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { unsigned long flags = 0; /* keep us warning-free. */ int rv; int run_to_completion; if ((msg->data_size >= 2) && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) && (msg->data[1] == IPMI_SEND_MSG_CMD) && (msg->user_data == NULL)) { /* * This is the local response to a command send, start * the timer for these. The user_data will not be * NULL if this is a response send, and we will let * response sends just go through. */ /* * Check for errors, if we get certain errors (ones * that mean basically we can try again later), we * ignore them and start the timer. Otherwise we * report the error immediately. */ if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0) && (msg->rsp[2] != IPMI_NODE_BUSY_ERR) && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR) && (msg->rsp[2] != IPMI_BUS_ERR) && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) { int chan = msg->rsp[3] & 0xf; /* Got an error sending the message, handle it. */ if (chan >= IPMI_MAX_CHANNELS) ; /* This shouldn't happen */ else if ((intf->channels[chan].medium == IPMI_CHANNEL_MEDIUM_8023LAN) || (intf->channels[chan].medium == IPMI_CHANNEL_MEDIUM_ASYNC)) ipmi_inc_stat(intf, sent_lan_command_errs); else ipmi_inc_stat(intf, sent_ipmb_command_errs); intf_err_seq(intf, msg->msgid, msg->rsp[2]); } else /* The message was sent, start the timer. */ intf_start_seq_timer(intf, msg->msgid); ipmi_free_smi_msg(msg); goto out; } /* * To preserve message order, if the list is not empty, we * tack this message onto the end of the list. */ run_to_completion = intf->run_to_completion; if (!run_to_completion) spin_lock_irqsave(&intf->waiting_msgs_lock, flags); if (!list_empty(&intf->waiting_msgs)) { list_add_tail(&msg->link, &intf->waiting_msgs); if (!run_to_completion) spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); goto out; } if (!run_to_completion) spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); rv = handle_new_recv_msg(intf, msg); if (rv > 0) { /* * Could not handle the message now, just add it to a * list to handle later. */ run_to_completion = intf->run_to_completion; if (!run_to_completion) spin_lock_irqsave(&intf->waiting_msgs_lock, flags); list_add_tail(&msg->link, &intf->waiting_msgs); if (!run_to_completion) spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); } else if (rv == 0) { ipmi_free_smi_msg(msg); } out: return; } EXPORT_SYMBOL(ipmi_smi_msg_received); void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) { ipmi_user_t user; rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { if (!user->handler->ipmi_watchdog_pretimeout) continue; user->handler->ipmi_watchdog_pretimeout(user->handler_data); } rcu_read_unlock(); } EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); static struct ipmi_smi_msg * smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, unsigned char seq, long seqid) { struct ipmi_smi_msg *smi_msg = ipmi_alloc_smi_msg(); if (!smi_msg) /* * If we can't allocate the message, then just return, we * get 4 retries, so this should be ok. */ return NULL; memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len); smi_msg->data_size = recv_msg->msg.data_len; smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid); #ifdef DEBUG_MSGING { int m; printk("Resend: "); for (m = 0; m < smi_msg->data_size; m++) printk(" %2.2x", smi_msg->data[m]); printk("\n"); } #endif return smi_msg; } static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, struct list_head *timeouts, long timeout_period, int slot, unsigned long *flags) { struct ipmi_recv_msg *msg; struct ipmi_smi_handlers *handlers; if (intf->intf_num == -1) return; if (!ent->inuse) return; ent->timeout -= timeout_period; if (ent->timeout > 0) return; if (ent->retries_left == 0) { /* The message has used all its retries. */ ent->inuse = 0; msg = ent->recv_msg; list_add_tail(&msg->link, timeouts); if (ent->broadcast) ipmi_inc_stat(intf, timed_out_ipmb_broadcasts); else if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE) ipmi_inc_stat(intf, timed_out_lan_commands); else ipmi_inc_stat(intf, timed_out_ipmb_commands); } else { struct ipmi_smi_msg *smi_msg; /* More retries, send again. */ /* * Start with the max timer, set to normal timer after * the message is sent. */ ent->timeout = MAX_MSG_TIMEOUT; ent->retries_left--; if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE) ipmi_inc_stat(intf, retransmitted_lan_commands); else ipmi_inc_stat(intf, retransmitted_ipmb_commands); smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot, ent->seqid); if (!smi_msg) return; spin_unlock_irqrestore(&intf->seq_lock, *flags); /* * Send the new message. We send with a zero * priority. It timed out, I doubt time is that * critical now, and high priority messages are really * only for messages to the local MC, which don't get * resent. */ handlers = intf->handlers; if (handlers) intf->handlers->sender(intf->send_info, smi_msg, 0); else ipmi_free_smi_msg(smi_msg); spin_lock_irqsave(&intf->seq_lock, *flags); } } static void ipmi_timeout_handler(long timeout_period) { ipmi_smi_t intf; struct list_head timeouts; struct ipmi_recv_msg *msg, *msg2; struct ipmi_smi_msg *smi_msg, *smi_msg2; unsigned long flags; int i; rcu_read_lock(); list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { /* See if any waiting messages need to be processed. */ spin_lock_irqsave(&intf->waiting_msgs_lock, flags); list_for_each_entry_safe(smi_msg, smi_msg2, &intf->waiting_msgs, link) { if (!handle_new_recv_msg(intf, smi_msg)) { list_del(&smi_msg->link); ipmi_free_smi_msg(smi_msg); } else { /* * To preserve message order, quit if we * can't handle a message. */ break; } } spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); /* * Go through the seq table and find any messages that * have timed out, putting them in the timeouts * list. */ INIT_LIST_HEAD(&timeouts); spin_lock_irqsave(&intf->seq_lock, flags); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) check_msg_timeout(intf, &(intf->seq_table[i]), &timeouts, timeout_period, i, &flags); spin_unlock_irqrestore(&intf->seq_lock, flags); list_for_each_entry_safe(msg, msg2, &timeouts, link) deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE); /* * Maintenance mode handling. Check the timeout * optimistically before we claim the lock. It may * mean a timeout gets missed occasionally, but that * only means the timeout gets extended by one period * in that case. No big deal, and it avoids the lock * most of the time. */ if (intf->auto_maintenance_timeout > 0) { spin_lock_irqsave(&intf->maintenance_mode_lock, flags); if (intf->auto_maintenance_timeout > 0) { intf->auto_maintenance_timeout -= timeout_period; if (!intf->maintenance_mode && (intf->auto_maintenance_timeout <= 0)) { intf->maintenance_mode_enable = 0; maintenance_mode_update(intf); } } spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); } } rcu_read_unlock(); } static void ipmi_request_event(void) { ipmi_smi_t intf; struct ipmi_smi_handlers *handlers; rcu_read_lock(); /* * Called from the timer, no need to check if handlers is * valid. */ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { /* No event requests when in maintenance mode. */ if (intf->maintenance_mode_enable) continue; handlers = intf->handlers; if (handlers) handlers->request_events(intf->send_info); } rcu_read_unlock(); } static struct timer_list ipmi_timer; /* Call every ~100 ms. */ #define IPMI_TIMEOUT_TIME 100 /* How many jiffies does it take to get to the timeout time. */ #define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) /* * Request events from the queue every second (this is the number of * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the * future, IPMI will add a way to know immediately if an event is in * the queue and this silliness can go away. */ #define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) static atomic_t stop_operation; static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME; static void ipmi_timeout(unsigned long data) { if (atomic_read(&stop_operation)) return; ticks_to_req_ev--; if (ticks_to_req_ev == 0) { ipmi_request_event(); ticks_to_req_ev = IPMI_REQUEST_EV_TIME; } ipmi_timeout_handler(IPMI_TIMEOUT_TIME); mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } static atomic_t smi_msg_inuse_count = ATOMIC_INIT(0); static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0); /* FIXME - convert these to slabs. */ static void free_smi_msg(struct ipmi_smi_msg *msg) { atomic_dec(&smi_msg_inuse_count); kfree(msg); } struct ipmi_smi_msg *ipmi_alloc_smi_msg(void) { struct ipmi_smi_msg *rv; rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC); if (rv) { rv->done = free_smi_msg; rv->user_data = NULL; atomic_inc(&smi_msg_inuse_count); } return rv; } EXPORT_SYMBOL(ipmi_alloc_smi_msg); static void free_recv_msg(struct ipmi_recv_msg *msg) { atomic_dec(&recv_msg_inuse_count); kfree(msg); } struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) { struct ipmi_recv_msg *rv; rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC); if (rv) { rv->user = NULL; rv->done = free_recv_msg; atomic_inc(&recv_msg_inuse_count); } return rv; } void ipmi_free_recv_msg(struct ipmi_recv_msg *msg) { if (msg->user) kref_put(&msg->user->refcount, free_user); msg->done(msg); } EXPORT_SYMBOL(ipmi_free_recv_msg); #ifdef CONFIG_IPMI_PANIC_EVENT static void dummy_smi_done_handler(struct ipmi_smi_msg *msg) { } static void dummy_recv_done_handler(struct ipmi_recv_msg *msg) { } #ifdef CONFIG_IPMI_PANIC_STRING static void event_receiver_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) { if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_SENSOR_EVENT_RESPONSE) && (msg->msg.cmd == IPMI_GET_EVENT_RECEIVER_CMD) && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { /* A get event receiver command, save it. */ intf->event_receiver = msg->msg.data[1]; intf->event_receiver_lun = msg->msg.data[2] & 0x3; } } static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) { if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) && (msg->msg.cmd == IPMI_GET_DEVICE_ID_CMD) && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { /* * A get device id command, save if we are an event * receiver or generator. */ intf->local_sel_device = (msg->msg.data[6] >> 2) & 1; intf->local_event_generator = (msg->msg.data[6] >> 5) & 1; } } #endif static void send_panic_events(char *str) { struct kernel_ipmi_msg msg; ipmi_smi_t intf; unsigned char data[16]; struct ipmi_system_interface_addr *si; struct ipmi_addr addr; struct ipmi_smi_msg smi_msg; struct ipmi_recv_msg recv_msg; si = (struct ipmi_system_interface_addr *) &addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si->channel = IPMI_BMC_CHANNEL; si->lun = 0; /* Fill in an event telling that we have failed. */ msg.netfn = 0x04; /* Sensor or Event. */ msg.cmd = 2; /* Platform event command. */ msg.data = data; msg.data_len = 8; data[0] = 0x41; /* Kernel generator ID, IPMI table 5-4 */ data[1] = 0x03; /* This is for IPMI 1.0. */ data[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */ data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */ data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */ /* * Put a few breadcrumbs in. Hopefully later we can add more things * to make the panic events more useful. */ if (str) { data[3] = str[0]; data[6] = str[1]; data[7] = str[2]; } smi_msg.done = dummy_smi_done_handler; recv_msg.done = dummy_recv_done_handler; /* For every registered interface, send the event. */ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { if (!intf->handlers) /* Interface is not ready. */ continue; intf->run_to_completion = 1; /* Send the event announcing the panic. */ intf->handlers->set_run_to_completion(intf->send_info, 1); i_ipmi_request(NULL, intf, &addr, 0, &msg, intf, &smi_msg, &recv_msg, 0, intf->channels[0].address, intf->channels[0].lun, 0, 1); /* Don't retry, and don't wait. */ } #ifdef CONFIG_IPMI_PANIC_STRING /* * On every interface, dump a bunch of OEM event holding the * string. */ if (!str) return; /* For every registered interface, send the event. */ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { char *p = str; struct ipmi_ipmb_addr *ipmb; int j; if (intf->intf_num == -1) /* Interface was not ready yet. */ continue; /* * intf_num is used as an marker to tell if the * interface is valid. Thus we need a read barrier to * make sure data fetched before checking intf_num * won't be used. */ smp_rmb(); /* * First job here is to figure out where to send the * OEM events. There's no way in IPMI to send OEM * events using an event send command, so we have to * find the SEL to put them in and stick them in * there. */ /* Get capabilities from the get device id. */ intf->local_sel_device = 0; intf->local_event_generator = 0; intf->event_receiver = 0; /* Request the device info from the local MC. */ msg.netfn = IPMI_NETFN_APP_REQUEST; msg.cmd = IPMI_GET_DEVICE_ID_CMD; msg.data = NULL; msg.data_len = 0; intf->null_user_handler = device_id_fetcher; i_ipmi_request(NULL, intf, &addr, 0, &msg, intf, &smi_msg, &recv_msg, 0, intf->channels[0].address, intf->channels[0].lun, 0, 1); /* Don't retry, and don't wait. */ if (intf->local_event_generator) { /* Request the event receiver from the local MC. */ msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST; msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD; msg.data = NULL; msg.data_len = 0; intf->null_user_handler = event_receiver_fetcher; i_ipmi_request(NULL, intf, &addr, 0, &msg, intf, &smi_msg, &recv_msg, 0, intf->channels[0].address, intf->channels[0].lun, 0, 1); /* no retry, and no wait. */ } intf->null_user_handler = NULL; /* * Validate the event receiver. The low bit must not * be 1 (it must be a valid IPMB address), it cannot * be zero, and it must not be my address. */ if (((intf->event_receiver & 1) == 0) && (intf->event_receiver != 0) && (intf->event_receiver != intf->channels[0].address)) { /* * The event receiver is valid, send an IPMB * message. */ ipmb = (struct ipmi_ipmb_addr *) &addr; ipmb->addr_type = IPMI_IPMB_ADDR_TYPE; ipmb->channel = 0; /* FIXME - is this right? */ ipmb->lun = intf->event_receiver_lun; ipmb->slave_addr = intf->event_receiver; } else if (intf->local_sel_device) { /* * The event receiver was not valid (or was * me), but I am an SEL device, just dump it * in my SEL. */ si = (struct ipmi_system_interface_addr *) &addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si->channel = IPMI_BMC_CHANNEL; si->lun = 0; } else continue; /* No where to send the event. */ msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */ msg.cmd = IPMI_ADD_SEL_ENTRY_CMD; msg.data = data; msg.data_len = 16; j = 0; while (*p) { int size = strlen(p); if (size > 11) size = 11; data[0] = 0; data[1] = 0; data[2] = 0xf0; /* OEM event without timestamp. */ data[3] = intf->channels[0].address; data[4] = j++; /* sequence # */ /* * Always give 11 bytes, so strncpy will fill * it with zeroes for me. */ strncpy(data+5, p, 11); p += size; i_ipmi_request(NULL, intf, &addr, 0, &msg, intf, &smi_msg, &recv_msg, 0, intf->channels[0].address, intf->channels[0].lun, 0, 1); /* no retry, and no wait. */ } } #endif /* CONFIG_IPMI_PANIC_STRING */ } #endif /* CONFIG_IPMI_PANIC_EVENT */ static int has_panicked; static int panic_event(struct notifier_block *this, unsigned long event, void *ptr) { ipmi_smi_t intf; if (has_panicked) return NOTIFY_DONE; has_panicked = 1; /* For every registered interface, set it to run to completion. */ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { if (!intf->handlers) /* Interface is not ready. */ continue; intf->run_to_completion = 1; intf->handlers->set_run_to_completion(intf->send_info, 1); } #ifdef CONFIG_IPMI_PANIC_EVENT send_panic_events(ptr); #endif return NOTIFY_DONE; } static struct notifier_block panic_block = { .notifier_call = panic_event, .next = NULL, .priority = 200 /* priority: INT_MAX >= x >= 0 */ }; static int ipmi_init_msghandler(void) { int rv; if (initialized) return 0; rv = driver_register(&ipmidriver); if (rv) { printk(KERN_ERR PFX "Could not register IPMI driver\n"); return rv; } printk(KERN_INFO "ipmi message handler version " IPMI_DRIVER_VERSION "\n"); #ifdef CONFIG_PROC_FS proc_ipmi_root = proc_mkdir("ipmi", NULL); if (!proc_ipmi_root) { printk(KERN_ERR PFX "Unable to create IPMI proc dir"); return -ENOMEM; } proc_ipmi_root->owner = THIS_MODULE; #endif /* CONFIG_PROC_FS */ setup_timer(&ipmi_timer, ipmi_timeout, 0); mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); atomic_notifier_chain_register(&panic_notifier_list, &panic_block); initialized = 1; return 0; } static __init int ipmi_init_msghandler_mod(void) { ipmi_init_msghandler(); return 0; } static __exit void cleanup_ipmi(void) { int count; if (!initialized) return; atomic_notifier_chain_unregister(&panic_notifier_list, &panic_block); /* * This can't be called if any interfaces exist, so no worry * about shutting down the interfaces. */ /* * Tell the timer to stop, then wait for it to stop. This * avoids problems with race conditions removing the timer * here. */ atomic_inc(&stop_operation); del_timer_sync(&ipmi_timer); #ifdef CONFIG_PROC_FS remove_proc_entry(proc_ipmi_root->name, NULL); #endif /* CONFIG_PROC_FS */ driver_unregister(&ipmidriver); initialized = 0; /* Check for buffer leaks. */ count = atomic_read(&smi_msg_inuse_count); if (count != 0) printk(KERN_WARNING PFX "SMI message count %d at exit\n", count); count = atomic_read(&recv_msg_inuse_count); if (count != 0) printk(KERN_WARNING PFX "recv message count %d at exit\n", count); } module_exit(cleanup_ipmi); module_init(ipmi_init_msghandler_mod); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Corey Minyard "); MODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI" " interface."); MODULE_VERSION(IPMI_DRIVER_VERSION);