162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ipmi_msghandler.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Incoming and outgoing message routing for an IPMI interface. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: MontaVista Software, Inc. 862306a36Sopenharmony_ci * Corey Minyard <minyard@mvista.com> 962306a36Sopenharmony_ci * source@mvista.com 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright 2002 MontaVista Software Inc. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define pr_fmt(fmt) "IPMI message handler: " fmt 1562306a36Sopenharmony_ci#define dev_fmt(fmt) pr_fmt(fmt) 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/panic_notifier.h> 2062306a36Sopenharmony_ci#include <linux/poll.h> 2162306a36Sopenharmony_ci#include <linux/sched.h> 2262306a36Sopenharmony_ci#include <linux/seq_file.h> 2362306a36Sopenharmony_ci#include <linux/spinlock.h> 2462306a36Sopenharmony_ci#include <linux/mutex.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/ipmi.h> 2762306a36Sopenharmony_ci#include <linux/ipmi_smi.h> 2862306a36Sopenharmony_ci#include <linux/notifier.h> 2962306a36Sopenharmony_ci#include <linux/init.h> 3062306a36Sopenharmony_ci#include <linux/proc_fs.h> 3162306a36Sopenharmony_ci#include <linux/rcupdate.h> 3262306a36Sopenharmony_ci#include <linux/interrupt.h> 3362306a36Sopenharmony_ci#include <linux/moduleparam.h> 3462306a36Sopenharmony_ci#include <linux/workqueue.h> 3562306a36Sopenharmony_ci#include <linux/uuid.h> 3662306a36Sopenharmony_ci#include <linux/nospec.h> 3762306a36Sopenharmony_ci#include <linux/vmalloc.h> 3862306a36Sopenharmony_ci#include <linux/delay.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define IPMI_DRIVER_VERSION "39.2" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); 4362306a36Sopenharmony_cistatic int ipmi_init_msghandler(void); 4462306a36Sopenharmony_cistatic void smi_recv_tasklet(struct tasklet_struct *t); 4562306a36Sopenharmony_cistatic void handle_new_recv_msgs(struct ipmi_smi *intf); 4662306a36Sopenharmony_cistatic void need_waiter(struct ipmi_smi *intf); 4762306a36Sopenharmony_cistatic int handle_one_recv_msg(struct ipmi_smi *intf, 4862306a36Sopenharmony_ci struct ipmi_smi_msg *msg); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic bool initialized; 5162306a36Sopenharmony_cistatic bool drvregistered; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Numbers in this enumerator should be mapped to ipmi_panic_event_str */ 5462306a36Sopenharmony_cienum ipmi_panic_event_op { 5562306a36Sopenharmony_ci IPMI_SEND_PANIC_EVENT_NONE, 5662306a36Sopenharmony_ci IPMI_SEND_PANIC_EVENT, 5762306a36Sopenharmony_ci IPMI_SEND_PANIC_EVENT_STRING, 5862306a36Sopenharmony_ci IPMI_SEND_PANIC_EVENT_MAX 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Indices in this array should be mapped to enum ipmi_panic_event_op */ 6262306a36Sopenharmony_cistatic const char *const ipmi_panic_event_str[] = { "none", "event", "string", NULL }; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#ifdef CONFIG_IPMI_PANIC_STRING 6562306a36Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_STRING 6662306a36Sopenharmony_ci#elif defined(CONFIG_IPMI_PANIC_EVENT) 6762306a36Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT 6862306a36Sopenharmony_ci#else 6962306a36Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_NONE 7062306a36Sopenharmony_ci#endif 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic enum ipmi_panic_event_op ipmi_send_panic_event = IPMI_PANIC_DEFAULT; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int panic_op_write_handler(const char *val, 7562306a36Sopenharmony_ci const struct kernel_param *kp) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci char valcp[16]; 7862306a36Sopenharmony_ci int e; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci strscpy(valcp, val, sizeof(valcp)); 8162306a36Sopenharmony_ci e = match_string(ipmi_panic_event_str, -1, strstrip(valcp)); 8262306a36Sopenharmony_ci if (e < 0) 8362306a36Sopenharmony_ci return e; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ipmi_send_panic_event = e; 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int panic_op_read_handler(char *buffer, const struct kernel_param *kp) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci const char *event_str; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (ipmi_send_panic_event >= IPMI_SEND_PANIC_EVENT_MAX) 9462306a36Sopenharmony_ci event_str = "???"; 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci event_str = ipmi_panic_event_str[ipmi_send_panic_event]; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return sprintf(buffer, "%s\n", event_str); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic const struct kernel_param_ops panic_op_ops = { 10262306a36Sopenharmony_ci .set = panic_op_write_handler, 10362306a36Sopenharmony_ci .get = panic_op_read_handler 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_cimodule_param_cb(panic_op, &panic_op_ops, NULL, 0600); 10662306a36Sopenharmony_ciMODULE_PARM_DESC(panic_op, "Sets if the IPMI driver will attempt to store panic information in the event log in the event of a panic. Set to 'none' for no, 'event' for a single event, or 'string' for a generic event and the panic string in IPMI OEM events."); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define MAX_EVENTS_IN_QUEUE 25 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* Remain in auto-maintenance mode for this amount of time (in ms). */ 11262306a36Sopenharmony_cistatic unsigned long maintenance_mode_timeout_ms = 30000; 11362306a36Sopenharmony_cimodule_param(maintenance_mode_timeout_ms, ulong, 0644); 11462306a36Sopenharmony_ciMODULE_PARM_DESC(maintenance_mode_timeout_ms, 11562306a36Sopenharmony_ci "The time (milliseconds) after the last maintenance message that the connection stays in maintenance mode."); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * Don't let a message sit in a queue forever, always time it with at lest 11962306a36Sopenharmony_ci * the max message timer. This is in milliseconds. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci#define MAX_MSG_TIMEOUT 60000 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * Timeout times below are in milliseconds, and are done off a 1 12562306a36Sopenharmony_ci * second timer. So setting the value to 1000 would mean anything 12662306a36Sopenharmony_ci * between 0 and 1000ms. So really the only reasonable minimum 12762306a36Sopenharmony_ci * setting it 2000ms, which is between 1 and 2 seconds. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* The default timeout for message retries. */ 13162306a36Sopenharmony_cistatic unsigned long default_retry_ms = 2000; 13262306a36Sopenharmony_cimodule_param(default_retry_ms, ulong, 0644); 13362306a36Sopenharmony_ciMODULE_PARM_DESC(default_retry_ms, 13462306a36Sopenharmony_ci "The time (milliseconds) between retry sends"); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* The default timeout for maintenance mode message retries. */ 13762306a36Sopenharmony_cistatic unsigned long default_maintenance_retry_ms = 3000; 13862306a36Sopenharmony_cimodule_param(default_maintenance_retry_ms, ulong, 0644); 13962306a36Sopenharmony_ciMODULE_PARM_DESC(default_maintenance_retry_ms, 14062306a36Sopenharmony_ci "The time (milliseconds) between retry sends in maintenance mode"); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* The default maximum number of retries */ 14362306a36Sopenharmony_cistatic unsigned int default_max_retries = 4; 14462306a36Sopenharmony_cimodule_param(default_max_retries, uint, 0644); 14562306a36Sopenharmony_ciMODULE_PARM_DESC(default_max_retries, 14662306a36Sopenharmony_ci "The time (milliseconds) between retry sends in maintenance mode"); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* The default maximum number of users that may register. */ 14962306a36Sopenharmony_cistatic unsigned int max_users = 30; 15062306a36Sopenharmony_cimodule_param(max_users, uint, 0644); 15162306a36Sopenharmony_ciMODULE_PARM_DESC(max_users, 15262306a36Sopenharmony_ci "The most users that may use the IPMI stack at one time."); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* The default maximum number of message a user may have outstanding. */ 15562306a36Sopenharmony_cistatic unsigned int max_msgs_per_user = 100; 15662306a36Sopenharmony_cimodule_param(max_msgs_per_user, uint, 0644); 15762306a36Sopenharmony_ciMODULE_PARM_DESC(max_msgs_per_user, 15862306a36Sopenharmony_ci "The most message a user may have outstanding."); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* Call every ~1000 ms. */ 16162306a36Sopenharmony_ci#define IPMI_TIMEOUT_TIME 1000 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* How many jiffies does it take to get to the timeout time. */ 16462306a36Sopenharmony_ci#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* 16762306a36Sopenharmony_ci * Request events from the queue every second (this is the number of 16862306a36Sopenharmony_ci * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the 16962306a36Sopenharmony_ci * future, IPMI will add a way to know immediately if an event is in 17062306a36Sopenharmony_ci * the queue and this silliness can go away. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* How long should we cache dynamic device IDs? */ 17562306a36Sopenharmony_ci#define IPMI_DYN_DEV_ID_EXPIRY (10 * HZ) 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * The main "user" data structure. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistruct ipmi_user { 18162306a36Sopenharmony_ci struct list_head link; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * Set to NULL when the user is destroyed, a pointer to myself 18562306a36Sopenharmony_ci * so srcu_dereference can be used on it. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci struct ipmi_user *self; 18862306a36Sopenharmony_ci struct srcu_struct release_barrier; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci struct kref refcount; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* The upper layer that handles receive messages. */ 19362306a36Sopenharmony_ci const struct ipmi_user_hndl *handler; 19462306a36Sopenharmony_ci void *handler_data; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* The interface this user is bound to. */ 19762306a36Sopenharmony_ci struct ipmi_smi *intf; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Does this interface receive IPMI events? */ 20062306a36Sopenharmony_ci bool gets_events; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci atomic_t nr_msgs; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Free must run in process context for RCU cleanup. */ 20562306a36Sopenharmony_ci struct work_struct remove_work; 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic struct workqueue_struct *remove_work_wq; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user, int *index) 21162306a36Sopenharmony_ci __acquires(user->release_barrier) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct ipmi_user *ruser; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci *index = srcu_read_lock(&user->release_barrier); 21662306a36Sopenharmony_ci ruser = srcu_dereference(user->self, &user->release_barrier); 21762306a36Sopenharmony_ci if (!ruser) 21862306a36Sopenharmony_ci srcu_read_unlock(&user->release_barrier, *index); 21962306a36Sopenharmony_ci return ruser; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void release_ipmi_user(struct ipmi_user *user, int index) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci srcu_read_unlock(&user->release_barrier, index); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistruct cmd_rcvr { 22862306a36Sopenharmony_ci struct list_head link; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci struct ipmi_user *user; 23162306a36Sopenharmony_ci unsigned char netfn; 23262306a36Sopenharmony_ci unsigned char cmd; 23362306a36Sopenharmony_ci unsigned int chans; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* 23662306a36Sopenharmony_ci * This is used to form a linked lised during mass deletion. 23762306a36Sopenharmony_ci * Since this is in an RCU list, we cannot use the link above 23862306a36Sopenharmony_ci * or change any data until the RCU period completes. So we 23962306a36Sopenharmony_ci * use this next variable during mass deletion so we can have 24062306a36Sopenharmony_ci * a list and don't have to wait and restart the search on 24162306a36Sopenharmony_ci * every individual deletion of a command. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci struct cmd_rcvr *next; 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistruct seq_table { 24762306a36Sopenharmony_ci unsigned int inuse : 1; 24862306a36Sopenharmony_ci unsigned int broadcast : 1; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci unsigned long timeout; 25162306a36Sopenharmony_ci unsigned long orig_timeout; 25262306a36Sopenharmony_ci unsigned int retries_left; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * To verify on an incoming send message response that this is 25662306a36Sopenharmony_ci * the message that the response is for, we keep a sequence id 25762306a36Sopenharmony_ci * and increment it every time we send a message. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci long seqid; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * This is held so we can properly respond to the message on a 26362306a36Sopenharmony_ci * timeout, and it is used to hold the temporary data for 26462306a36Sopenharmony_ci * retransmission, too. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* 27062306a36Sopenharmony_ci * Store the information in a msgid (long) to allow us to find a 27162306a36Sopenharmony_ci * sequence table entry from the msgid. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci#define STORE_SEQ_IN_MSGID(seq, seqid) \ 27462306a36Sopenharmony_ci ((((seq) & 0x3f) << 26) | ((seqid) & 0x3ffffff)) 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \ 27762306a36Sopenharmony_ci do { \ 27862306a36Sopenharmony_ci seq = (((msgid) >> 26) & 0x3f); \ 27962306a36Sopenharmony_ci seqid = ((msgid) & 0x3ffffff); \ 28062306a36Sopenharmony_ci } while (0) 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff) 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci#define IPMI_MAX_CHANNELS 16 28562306a36Sopenharmony_cistruct ipmi_channel { 28662306a36Sopenharmony_ci unsigned char medium; 28762306a36Sopenharmony_ci unsigned char protocol; 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistruct ipmi_channel_set { 29162306a36Sopenharmony_ci struct ipmi_channel c[IPMI_MAX_CHANNELS]; 29262306a36Sopenharmony_ci}; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistruct ipmi_my_addrinfo { 29562306a36Sopenharmony_ci /* 29662306a36Sopenharmony_ci * My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, 29762306a36Sopenharmony_ci * but may be changed by the user. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci unsigned char address; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * My LUN. This should generally stay the SMS LUN, but just in 30362306a36Sopenharmony_ci * case... 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci unsigned char lun; 30662306a36Sopenharmony_ci}; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* 30962306a36Sopenharmony_ci * Note that the product id, manufacturer id, guid, and device id are 31062306a36Sopenharmony_ci * immutable in this structure, so dyn_mutex is not required for 31162306a36Sopenharmony_ci * accessing those. If those change on a BMC, a new BMC is allocated. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_cistruct bmc_device { 31462306a36Sopenharmony_ci struct platform_device pdev; 31562306a36Sopenharmony_ci struct list_head intfs; /* Interfaces on this BMC. */ 31662306a36Sopenharmony_ci struct ipmi_device_id id; 31762306a36Sopenharmony_ci struct ipmi_device_id fetch_id; 31862306a36Sopenharmony_ci int dyn_id_set; 31962306a36Sopenharmony_ci unsigned long dyn_id_expiry; 32062306a36Sopenharmony_ci struct mutex dyn_mutex; /* Protects id, intfs, & dyn* */ 32162306a36Sopenharmony_ci guid_t guid; 32262306a36Sopenharmony_ci guid_t fetch_guid; 32362306a36Sopenharmony_ci int dyn_guid_set; 32462306a36Sopenharmony_ci struct kref usecount; 32562306a36Sopenharmony_ci struct work_struct remove_work; 32662306a36Sopenharmony_ci unsigned char cc; /* completion code */ 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci#define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev) 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, 33162306a36Sopenharmony_ci struct ipmi_device_id *id, 33262306a36Sopenharmony_ci bool *guid_set, guid_t *guid); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/* 33562306a36Sopenharmony_ci * Various statistics for IPMI, these index stats[] in the ipmi_smi 33662306a36Sopenharmony_ci * structure. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_cienum ipmi_stat_indexes { 33962306a36Sopenharmony_ci /* Commands we got from the user that were invalid. */ 34062306a36Sopenharmony_ci IPMI_STAT_sent_invalid_commands = 0, 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Commands we sent to the MC. */ 34362306a36Sopenharmony_ci IPMI_STAT_sent_local_commands, 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Responses from the MC that were delivered to a user. */ 34662306a36Sopenharmony_ci IPMI_STAT_handled_local_responses, 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Responses from the MC that were not delivered to a user. */ 34962306a36Sopenharmony_ci IPMI_STAT_unhandled_local_responses, 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Commands we sent out to the IPMB bus. */ 35262306a36Sopenharmony_ci IPMI_STAT_sent_ipmb_commands, 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Commands sent on the IPMB that had errors on the SEND CMD */ 35562306a36Sopenharmony_ci IPMI_STAT_sent_ipmb_command_errs, 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Each retransmit increments this count. */ 35862306a36Sopenharmony_ci IPMI_STAT_retransmitted_ipmb_commands, 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* 36162306a36Sopenharmony_ci * When a message times out (runs out of retransmits) this is 36262306a36Sopenharmony_ci * incremented. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci IPMI_STAT_timed_out_ipmb_commands, 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* 36762306a36Sopenharmony_ci * This is like above, but for broadcasts. Broadcasts are 36862306a36Sopenharmony_ci * *not* included in the above count (they are expected to 36962306a36Sopenharmony_ci * time out). 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci IPMI_STAT_timed_out_ipmb_broadcasts, 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Responses I have sent to the IPMB bus. */ 37462306a36Sopenharmony_ci IPMI_STAT_sent_ipmb_responses, 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* The response was delivered to the user. */ 37762306a36Sopenharmony_ci IPMI_STAT_handled_ipmb_responses, 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* The response had invalid data in it. */ 38062306a36Sopenharmony_ci IPMI_STAT_invalid_ipmb_responses, 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* The response didn't have anyone waiting for it. */ 38362306a36Sopenharmony_ci IPMI_STAT_unhandled_ipmb_responses, 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* Commands we sent out to the IPMB bus. */ 38662306a36Sopenharmony_ci IPMI_STAT_sent_lan_commands, 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Commands sent on the IPMB that had errors on the SEND CMD */ 38962306a36Sopenharmony_ci IPMI_STAT_sent_lan_command_errs, 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Each retransmit increments this count. */ 39262306a36Sopenharmony_ci IPMI_STAT_retransmitted_lan_commands, 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * When a message times out (runs out of retransmits) this is 39662306a36Sopenharmony_ci * incremented. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci IPMI_STAT_timed_out_lan_commands, 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Responses I have sent to the IPMB bus. */ 40162306a36Sopenharmony_ci IPMI_STAT_sent_lan_responses, 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* The response was delivered to the user. */ 40462306a36Sopenharmony_ci IPMI_STAT_handled_lan_responses, 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* The response had invalid data in it. */ 40762306a36Sopenharmony_ci IPMI_STAT_invalid_lan_responses, 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* The response didn't have anyone waiting for it. */ 41062306a36Sopenharmony_ci IPMI_STAT_unhandled_lan_responses, 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* The command was delivered to the user. */ 41362306a36Sopenharmony_ci IPMI_STAT_handled_commands, 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* The command had invalid data in it. */ 41662306a36Sopenharmony_ci IPMI_STAT_invalid_commands, 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* The command didn't have anyone waiting for it. */ 41962306a36Sopenharmony_ci IPMI_STAT_unhandled_commands, 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Invalid data in an event. */ 42262306a36Sopenharmony_ci IPMI_STAT_invalid_events, 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Events that were received with the proper format. */ 42562306a36Sopenharmony_ci IPMI_STAT_events, 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Retransmissions on IPMB that failed. */ 42862306a36Sopenharmony_ci IPMI_STAT_dropped_rexmit_ipmb_commands, 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Retransmissions on LAN that failed. */ 43162306a36Sopenharmony_ci IPMI_STAT_dropped_rexmit_lan_commands, 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* This *must* remain last, add new values above this. */ 43462306a36Sopenharmony_ci IPMI_NUM_STATS 43562306a36Sopenharmony_ci}; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci#define IPMI_IPMB_NUM_SEQ 64 43962306a36Sopenharmony_cistruct ipmi_smi { 44062306a36Sopenharmony_ci struct module *owner; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* What interface number are we? */ 44362306a36Sopenharmony_ci int intf_num; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci struct kref refcount; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Set when the interface is being unregistered. */ 44862306a36Sopenharmony_ci bool in_shutdown; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Used for a list of interfaces. */ 45162306a36Sopenharmony_ci struct list_head link; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * The list of upper layers that are using me. seq_lock write 45562306a36Sopenharmony_ci * protects this. Read protection is with srcu. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci struct list_head users; 45862306a36Sopenharmony_ci struct srcu_struct users_srcu; 45962306a36Sopenharmony_ci atomic_t nr_users; 46062306a36Sopenharmony_ci struct device_attribute nr_users_devattr; 46162306a36Sopenharmony_ci struct device_attribute nr_msgs_devattr; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Used for wake ups at startup. */ 46562306a36Sopenharmony_ci wait_queue_head_t waitq; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * Prevents the interface from being unregistered when the 46962306a36Sopenharmony_ci * interface is used by being looked up through the BMC 47062306a36Sopenharmony_ci * structure. 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci struct mutex bmc_reg_mutex; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci struct bmc_device tmp_bmc; 47562306a36Sopenharmony_ci struct bmc_device *bmc; 47662306a36Sopenharmony_ci bool bmc_registered; 47762306a36Sopenharmony_ci struct list_head bmc_link; 47862306a36Sopenharmony_ci char *my_dev_name; 47962306a36Sopenharmony_ci bool in_bmc_register; /* Handle recursive situations. Yuck. */ 48062306a36Sopenharmony_ci struct work_struct bmc_reg_work; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci const struct ipmi_smi_handlers *handlers; 48362306a36Sopenharmony_ci void *send_info; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Driver-model device for the system interface. */ 48662306a36Sopenharmony_ci struct device *si_dev; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* 48962306a36Sopenharmony_ci * A table of sequence numbers for this interface. We use the 49062306a36Sopenharmony_ci * sequence numbers for IPMB messages that go out of the 49162306a36Sopenharmony_ci * interface to match them up with their responses. A routine 49262306a36Sopenharmony_ci * is called periodically to time the items in this list. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci spinlock_t seq_lock; 49562306a36Sopenharmony_ci struct seq_table seq_table[IPMI_IPMB_NUM_SEQ]; 49662306a36Sopenharmony_ci int curr_seq; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* 49962306a36Sopenharmony_ci * Messages queued for delivery. If delivery fails (out of memory 50062306a36Sopenharmony_ci * for instance), They will stay in here to be processed later in a 50162306a36Sopenharmony_ci * periodic timer interrupt. The tasklet is for handling received 50262306a36Sopenharmony_ci * messages directly from the handler. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci spinlock_t waiting_rcv_msgs_lock; 50562306a36Sopenharmony_ci struct list_head waiting_rcv_msgs; 50662306a36Sopenharmony_ci atomic_t watchdog_pretimeouts_to_deliver; 50762306a36Sopenharmony_ci struct tasklet_struct recv_tasklet; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci spinlock_t xmit_msgs_lock; 51062306a36Sopenharmony_ci struct list_head xmit_msgs; 51162306a36Sopenharmony_ci struct ipmi_smi_msg *curr_msg; 51262306a36Sopenharmony_ci struct list_head hp_xmit_msgs; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* 51562306a36Sopenharmony_ci * The list of command receivers that are registered for commands 51662306a36Sopenharmony_ci * on this interface. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci struct mutex cmd_rcvrs_mutex; 51962306a36Sopenharmony_ci struct list_head cmd_rcvrs; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * Events that were queues because no one was there to receive 52362306a36Sopenharmony_ci * them. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci spinlock_t events_lock; /* For dealing with event stuff. */ 52662306a36Sopenharmony_ci struct list_head waiting_events; 52762306a36Sopenharmony_ci unsigned int waiting_events_count; /* How many events in queue? */ 52862306a36Sopenharmony_ci char delivering_events; 52962306a36Sopenharmony_ci char event_msg_printed; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* How many users are waiting for events? */ 53262306a36Sopenharmony_ci atomic_t event_waiters; 53362306a36Sopenharmony_ci unsigned int ticks_to_req_ev; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci spinlock_t watch_lock; /* For dealing with watch stuff below. */ 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* How many users are waiting for commands? */ 53862306a36Sopenharmony_ci unsigned int command_waiters; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* How many users are waiting for watchdogs? */ 54162306a36Sopenharmony_ci unsigned int watchdog_waiters; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* How many users are waiting for message responses? */ 54462306a36Sopenharmony_ci unsigned int response_waiters; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* 54762306a36Sopenharmony_ci * Tells what the lower layer has last been asked to watch for, 54862306a36Sopenharmony_ci * messages and/or watchdogs. Protected by watch_lock. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci unsigned int last_watch_mask; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* 55362306a36Sopenharmony_ci * The event receiver for my BMC, only really used at panic 55462306a36Sopenharmony_ci * shutdown as a place to store this. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci unsigned char event_receiver; 55762306a36Sopenharmony_ci unsigned char event_receiver_lun; 55862306a36Sopenharmony_ci unsigned char local_sel_device; 55962306a36Sopenharmony_ci unsigned char local_event_generator; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* For handling of maintenance mode. */ 56262306a36Sopenharmony_ci int maintenance_mode; 56362306a36Sopenharmony_ci bool maintenance_mode_enable; 56462306a36Sopenharmony_ci int auto_maintenance_timeout; 56562306a36Sopenharmony_ci spinlock_t maintenance_mode_lock; /* Used in a timer... */ 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* 56862306a36Sopenharmony_ci * If we are doing maintenance on something on IPMB, extend 56962306a36Sopenharmony_ci * the timeout time to avoid timeouts writing firmware and 57062306a36Sopenharmony_ci * such. 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci int ipmb_maintenance_mode_timeout; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * A cheap hack, if this is non-null and a message to an 57662306a36Sopenharmony_ci * interface comes in with a NULL user, call this routine with 57762306a36Sopenharmony_ci * it. Note that the message will still be freed by the 57862306a36Sopenharmony_ci * caller. This only works on the system interface. 57962306a36Sopenharmony_ci * 58062306a36Sopenharmony_ci * Protected by bmc_reg_mutex. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci void (*null_user_handler)(struct ipmi_smi *intf, 58362306a36Sopenharmony_ci struct ipmi_recv_msg *msg); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 58662306a36Sopenharmony_ci * When we are scanning the channels for an SMI, this will 58762306a36Sopenharmony_ci * tell which channel we are scanning. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_ci int curr_channel; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Channel information */ 59262306a36Sopenharmony_ci struct ipmi_channel_set *channel_list; 59362306a36Sopenharmony_ci unsigned int curr_working_cset; /* First index into the following. */ 59462306a36Sopenharmony_ci struct ipmi_channel_set wchannels[2]; 59562306a36Sopenharmony_ci struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS]; 59662306a36Sopenharmony_ci bool channels_ready; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci atomic_t stats[IPMI_NUM_STATS]; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* 60162306a36Sopenharmony_ci * run_to_completion duplicate of smb_info, smi_info 60262306a36Sopenharmony_ci * and ipmi_serial_info structures. Used to decrease numbers of 60362306a36Sopenharmony_ci * parameters passed by "low" level IPMI code. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci int run_to_completion; 60662306a36Sopenharmony_ci}; 60762306a36Sopenharmony_ci#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void __get_guid(struct ipmi_smi *intf); 61062306a36Sopenharmony_cistatic void __ipmi_bmc_unregister(struct ipmi_smi *intf); 61162306a36Sopenharmony_cistatic int __ipmi_bmc_register(struct ipmi_smi *intf, 61262306a36Sopenharmony_ci struct ipmi_device_id *id, 61362306a36Sopenharmony_ci bool guid_set, guid_t *guid, int intf_num); 61462306a36Sopenharmony_cistatic int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* 61862306a36Sopenharmony_ci * The driver model view of the IPMI messaging driver. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_cistatic struct platform_driver ipmidriver = { 62162306a36Sopenharmony_ci .driver = { 62262306a36Sopenharmony_ci .name = "ipmi", 62362306a36Sopenharmony_ci .bus = &platform_bus_type 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ci/* 62762306a36Sopenharmony_ci * This mutex keeps us from adding the same BMC twice. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_cistatic DEFINE_MUTEX(ipmidriver_mutex); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic LIST_HEAD(ipmi_interfaces); 63262306a36Sopenharmony_cistatic DEFINE_MUTEX(ipmi_interfaces_mutex); 63362306a36Sopenharmony_ci#define ipmi_interfaces_mutex_held() \ 63462306a36Sopenharmony_ci lockdep_is_held(&ipmi_interfaces_mutex) 63562306a36Sopenharmony_cistatic struct srcu_struct ipmi_interfaces_srcu; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* 63862306a36Sopenharmony_ci * List of watchers that want to know when smi's are added and deleted. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_cistatic LIST_HEAD(smi_watchers); 64162306a36Sopenharmony_cistatic DEFINE_MUTEX(smi_watchers_mutex); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci#define ipmi_inc_stat(intf, stat) \ 64462306a36Sopenharmony_ci atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat]) 64562306a36Sopenharmony_ci#define ipmi_get_stat(intf, stat) \ 64662306a36Sopenharmony_ci ((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat])) 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic const char * const addr_src_to_str[] = { 64962306a36Sopenharmony_ci "invalid", "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI", 65062306a36Sopenharmony_ci "device-tree", "platform" 65162306a36Sopenharmony_ci}; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ciconst char *ipmi_addr_src_to_str(enum ipmi_addr_src src) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci if (src >= SI_LAST) 65662306a36Sopenharmony_ci src = 0; /* Invalid */ 65762306a36Sopenharmony_ci return addr_src_to_str[src]; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_addr_src_to_str); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int is_lan_addr(struct ipmi_addr *addr) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci return addr->addr_type == IPMI_LAN_ADDR_TYPE; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int is_ipmb_addr(struct ipmi_addr *addr) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci return addr->addr_type == IPMI_IPMB_ADDR_TYPE; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic int is_ipmb_bcast_addr(struct ipmi_addr *addr) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic int is_ipmb_direct_addr(struct ipmi_addr *addr) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci return addr->addr_type == IPMI_IPMB_DIRECT_ADDR_TYPE; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void free_recv_msg_list(struct list_head *q) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct ipmi_recv_msg *msg, *msg2; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci list_for_each_entry_safe(msg, msg2, q, link) { 68662306a36Sopenharmony_ci list_del(&msg->link); 68762306a36Sopenharmony_ci ipmi_free_recv_msg(msg); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic void free_smi_msg_list(struct list_head *q) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct ipmi_smi_msg *msg, *msg2; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci list_for_each_entry_safe(msg, msg2, q, link) { 69662306a36Sopenharmony_ci list_del(&msg->link); 69762306a36Sopenharmony_ci ipmi_free_smi_msg(msg); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic void clean_up_interface_data(struct ipmi_smi *intf) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci int i; 70462306a36Sopenharmony_ci struct cmd_rcvr *rcvr, *rcvr2; 70562306a36Sopenharmony_ci struct list_head list; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci tasklet_kill(&intf->recv_tasklet); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci free_smi_msg_list(&intf->waiting_rcv_msgs); 71062306a36Sopenharmony_ci free_recv_msg_list(&intf->waiting_events); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* 71362306a36Sopenharmony_ci * Wholesale remove all the entries from the list in the 71462306a36Sopenharmony_ci * interface and wait for RCU to know that none are in use. 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci mutex_lock(&intf->cmd_rcvrs_mutex); 71762306a36Sopenharmony_ci INIT_LIST_HEAD(&list); 71862306a36Sopenharmony_ci list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu); 71962306a36Sopenharmony_ci mutex_unlock(&intf->cmd_rcvrs_mutex); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci list_for_each_entry_safe(rcvr, rcvr2, &list, link) 72262306a36Sopenharmony_ci kfree(rcvr); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { 72562306a36Sopenharmony_ci if ((intf->seq_table[i].inuse) 72662306a36Sopenharmony_ci && (intf->seq_table[i].recv_msg)) 72762306a36Sopenharmony_ci ipmi_free_recv_msg(intf->seq_table[i].recv_msg); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic void intf_free(struct kref *ref) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci clean_up_interface_data(intf); 73662306a36Sopenharmony_ci kfree(intf); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ciint ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct ipmi_smi *intf; 74262306a36Sopenharmony_ci int index, rv; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* 74562306a36Sopenharmony_ci * Make sure the driver is actually initialized, this handles 74662306a36Sopenharmony_ci * problems with initialization order. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_ci rv = ipmi_init_msghandler(); 74962306a36Sopenharmony_ci if (rv) 75062306a36Sopenharmony_ci return rv; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci mutex_lock(&smi_watchers_mutex); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci list_add(&watcher->link, &smi_watchers); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci index = srcu_read_lock(&ipmi_interfaces_srcu); 75762306a36Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link, 75862306a36Sopenharmony_ci lockdep_is_held(&smi_watchers_mutex)) { 75962306a36Sopenharmony_ci int intf_num = READ_ONCE(intf->intf_num); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (intf_num == -1) 76262306a36Sopenharmony_ci continue; 76362306a36Sopenharmony_ci watcher->new_smi(intf_num, intf->si_dev); 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci mutex_unlock(&smi_watchers_mutex); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watcher_register); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ciint ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci mutex_lock(&smi_watchers_mutex); 77662306a36Sopenharmony_ci list_del(&watcher->link); 77762306a36Sopenharmony_ci mutex_unlock(&smi_watchers_mutex); 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watcher_unregister); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci/* 78362306a36Sopenharmony_ci * Must be called with smi_watchers_mutex held. 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_cistatic void 78662306a36Sopenharmony_cicall_smi_watchers(int i, struct device *dev) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct ipmi_smi_watcher *w; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci mutex_lock(&smi_watchers_mutex); 79162306a36Sopenharmony_ci list_for_each_entry(w, &smi_watchers, link) { 79262306a36Sopenharmony_ci if (try_module_get(w->owner)) { 79362306a36Sopenharmony_ci w->new_smi(i, dev); 79462306a36Sopenharmony_ci module_put(w->owner); 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci mutex_unlock(&smi_watchers_mutex); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int 80162306a36Sopenharmony_ciipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci if (addr1->addr_type != addr2->addr_type) 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (addr1->channel != addr2->channel) 80762306a36Sopenharmony_ci return 0; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { 81062306a36Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr1 81162306a36Sopenharmony_ci = (struct ipmi_system_interface_addr *) addr1; 81262306a36Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr2 81362306a36Sopenharmony_ci = (struct ipmi_system_interface_addr *) addr2; 81462306a36Sopenharmony_ci return (smi_addr1->lun == smi_addr2->lun); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (is_ipmb_addr(addr1) || is_ipmb_bcast_addr(addr1)) { 81862306a36Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr1 81962306a36Sopenharmony_ci = (struct ipmi_ipmb_addr *) addr1; 82062306a36Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr2 82162306a36Sopenharmony_ci = (struct ipmi_ipmb_addr *) addr2; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr) 82462306a36Sopenharmony_ci && (ipmb_addr1->lun == ipmb_addr2->lun)); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (is_ipmb_direct_addr(addr1)) { 82862306a36Sopenharmony_ci struct ipmi_ipmb_direct_addr *daddr1 82962306a36Sopenharmony_ci = (struct ipmi_ipmb_direct_addr *) addr1; 83062306a36Sopenharmony_ci struct ipmi_ipmb_direct_addr *daddr2 83162306a36Sopenharmony_ci = (struct ipmi_ipmb_direct_addr *) addr2; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci return daddr1->slave_addr == daddr2->slave_addr && 83462306a36Sopenharmony_ci daddr1->rq_lun == daddr2->rq_lun && 83562306a36Sopenharmony_ci daddr1->rs_lun == daddr2->rs_lun; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (is_lan_addr(addr1)) { 83962306a36Sopenharmony_ci struct ipmi_lan_addr *lan_addr1 84062306a36Sopenharmony_ci = (struct ipmi_lan_addr *) addr1; 84162306a36Sopenharmony_ci struct ipmi_lan_addr *lan_addr2 84262306a36Sopenharmony_ci = (struct ipmi_lan_addr *) addr2; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return ((lan_addr1->remote_SWID == lan_addr2->remote_SWID) 84562306a36Sopenharmony_ci && (lan_addr1->local_SWID == lan_addr2->local_SWID) 84662306a36Sopenharmony_ci && (lan_addr1->session_handle 84762306a36Sopenharmony_ci == lan_addr2->session_handle) 84862306a36Sopenharmony_ci && (lan_addr1->lun == lan_addr2->lun)); 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return 1; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ciint ipmi_validate_addr(struct ipmi_addr *addr, int len) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci if (len < sizeof(struct ipmi_system_interface_addr)) 85762306a36Sopenharmony_ci return -EINVAL; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { 86062306a36Sopenharmony_ci if (addr->channel != IPMI_BMC_CHANNEL) 86162306a36Sopenharmony_ci return -EINVAL; 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if ((addr->channel == IPMI_BMC_CHANNEL) 86662306a36Sopenharmony_ci || (addr->channel >= IPMI_MAX_CHANNELS) 86762306a36Sopenharmony_ci || (addr->channel < 0)) 86862306a36Sopenharmony_ci return -EINVAL; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { 87162306a36Sopenharmony_ci if (len < sizeof(struct ipmi_ipmb_addr)) 87262306a36Sopenharmony_ci return -EINVAL; 87362306a36Sopenharmony_ci return 0; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (is_ipmb_direct_addr(addr)) { 87762306a36Sopenharmony_ci struct ipmi_ipmb_direct_addr *daddr = (void *) addr; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (addr->channel != 0) 88062306a36Sopenharmony_ci return -EINVAL; 88162306a36Sopenharmony_ci if (len < sizeof(struct ipmi_ipmb_direct_addr)) 88262306a36Sopenharmony_ci return -EINVAL; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (daddr->slave_addr & 0x01) 88562306a36Sopenharmony_ci return -EINVAL; 88662306a36Sopenharmony_ci if (daddr->rq_lun >= 4) 88762306a36Sopenharmony_ci return -EINVAL; 88862306a36Sopenharmony_ci if (daddr->rs_lun >= 4) 88962306a36Sopenharmony_ci return -EINVAL; 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (is_lan_addr(addr)) { 89462306a36Sopenharmony_ci if (len < sizeof(struct ipmi_lan_addr)) 89562306a36Sopenharmony_ci return -EINVAL; 89662306a36Sopenharmony_ci return 0; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return -EINVAL; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_validate_addr); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ciunsigned int ipmi_addr_length(int addr_type) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 90662306a36Sopenharmony_ci return sizeof(struct ipmi_system_interface_addr); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if ((addr_type == IPMI_IPMB_ADDR_TYPE) 90962306a36Sopenharmony_ci || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) 91062306a36Sopenharmony_ci return sizeof(struct ipmi_ipmb_addr); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (addr_type == IPMI_IPMB_DIRECT_ADDR_TYPE) 91362306a36Sopenharmony_ci return sizeof(struct ipmi_ipmb_direct_addr); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (addr_type == IPMI_LAN_ADDR_TYPE) 91662306a36Sopenharmony_ci return sizeof(struct ipmi_lan_addr); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_addr_length); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci int rv = 0; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (!msg->user) { 92762306a36Sopenharmony_ci /* Special handling for NULL users. */ 92862306a36Sopenharmony_ci if (intf->null_user_handler) { 92962306a36Sopenharmony_ci intf->null_user_handler(intf, msg); 93062306a36Sopenharmony_ci } else { 93162306a36Sopenharmony_ci /* No handler, so give up. */ 93262306a36Sopenharmony_ci rv = -EINVAL; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci ipmi_free_recv_msg(msg); 93562306a36Sopenharmony_ci } else if (oops_in_progress) { 93662306a36Sopenharmony_ci /* 93762306a36Sopenharmony_ci * If we are running in the panic context, calling the 93862306a36Sopenharmony_ci * receive handler doesn't much meaning and has a deadlock 93962306a36Sopenharmony_ci * risk. At this moment, simply skip it in that case. 94062306a36Sopenharmony_ci */ 94162306a36Sopenharmony_ci ipmi_free_recv_msg(msg); 94262306a36Sopenharmony_ci atomic_dec(&msg->user->nr_msgs); 94362306a36Sopenharmony_ci } else { 94462306a36Sopenharmony_ci int index; 94562306a36Sopenharmony_ci struct ipmi_user *user = acquire_ipmi_user(msg->user, &index); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (user) { 94862306a36Sopenharmony_ci atomic_dec(&user->nr_msgs); 94962306a36Sopenharmony_ci user->handler->ipmi_recv_hndl(msg, user->handler_data); 95062306a36Sopenharmony_ci release_ipmi_user(user, index); 95162306a36Sopenharmony_ci } else { 95262306a36Sopenharmony_ci /* User went away, give up. */ 95362306a36Sopenharmony_ci ipmi_free_recv_msg(msg); 95462306a36Sopenharmony_ci rv = -EINVAL; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return rv; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic void deliver_local_response(struct ipmi_smi *intf, 96262306a36Sopenharmony_ci struct ipmi_recv_msg *msg) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci if (deliver_response(intf, msg)) 96562306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_local_responses); 96662306a36Sopenharmony_ci else 96762306a36Sopenharmony_ci ipmi_inc_stat(intf, handled_local_responses); 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistatic void deliver_err_response(struct ipmi_smi *intf, 97162306a36Sopenharmony_ci struct ipmi_recv_msg *msg, int err) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 97462306a36Sopenharmony_ci msg->msg_data[0] = err; 97562306a36Sopenharmony_ci msg->msg.netfn |= 1; /* Convert to a response. */ 97662306a36Sopenharmony_ci msg->msg.data_len = 1; 97762306a36Sopenharmony_ci msg->msg.data = msg->msg_data; 97862306a36Sopenharmony_ci deliver_local_response(intf, msg); 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic void smi_add_watch(struct ipmi_smi *intf, unsigned int flags) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci unsigned long iflags; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (!intf->handlers->set_need_watch) 98662306a36Sopenharmony_ci return; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci spin_lock_irqsave(&intf->watch_lock, iflags); 98962306a36Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES) 99062306a36Sopenharmony_ci intf->response_waiters++; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG) 99362306a36Sopenharmony_ci intf->watchdog_waiters++; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS) 99662306a36Sopenharmony_ci intf->command_waiters++; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if ((intf->last_watch_mask & flags) != flags) { 99962306a36Sopenharmony_ci intf->last_watch_mask |= flags; 100062306a36Sopenharmony_ci intf->handlers->set_need_watch(intf->send_info, 100162306a36Sopenharmony_ci intf->last_watch_mask); 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->watch_lock, iflags); 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic void smi_remove_watch(struct ipmi_smi *intf, unsigned int flags) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci unsigned long iflags; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (!intf->handlers->set_need_watch) 101162306a36Sopenharmony_ci return; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci spin_lock_irqsave(&intf->watch_lock, iflags); 101462306a36Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES) 101562306a36Sopenharmony_ci intf->response_waiters--; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG) 101862306a36Sopenharmony_ci intf->watchdog_waiters--; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS) 102162306a36Sopenharmony_ci intf->command_waiters--; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci flags = 0; 102462306a36Sopenharmony_ci if (intf->response_waiters) 102562306a36Sopenharmony_ci flags |= IPMI_WATCH_MASK_CHECK_MESSAGES; 102662306a36Sopenharmony_ci if (intf->watchdog_waiters) 102762306a36Sopenharmony_ci flags |= IPMI_WATCH_MASK_CHECK_WATCHDOG; 102862306a36Sopenharmony_ci if (intf->command_waiters) 102962306a36Sopenharmony_ci flags |= IPMI_WATCH_MASK_CHECK_COMMANDS; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (intf->last_watch_mask != flags) { 103262306a36Sopenharmony_ci intf->last_watch_mask = flags; 103362306a36Sopenharmony_ci intf->handlers->set_need_watch(intf->send_info, 103462306a36Sopenharmony_ci intf->last_watch_mask); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->watch_lock, iflags); 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci/* 104062306a36Sopenharmony_ci * Find the next sequence number not being used and add the given 104162306a36Sopenharmony_ci * message with the given timeout to the sequence table. This must be 104262306a36Sopenharmony_ci * called with the interface's seq_lock held. 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_cistatic int intf_next_seq(struct ipmi_smi *intf, 104562306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 104662306a36Sopenharmony_ci unsigned long timeout, 104762306a36Sopenharmony_ci int retries, 104862306a36Sopenharmony_ci int broadcast, 104962306a36Sopenharmony_ci unsigned char *seq, 105062306a36Sopenharmony_ci long *seqid) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci int rv = 0; 105362306a36Sopenharmony_ci unsigned int i; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci if (timeout == 0) 105662306a36Sopenharmony_ci timeout = default_retry_ms; 105762306a36Sopenharmony_ci if (retries < 0) 105862306a36Sopenharmony_ci retries = default_max_retries; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; 106162306a36Sopenharmony_ci i = (i+1)%IPMI_IPMB_NUM_SEQ) { 106262306a36Sopenharmony_ci if (!intf->seq_table[i].inuse) 106362306a36Sopenharmony_ci break; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (!intf->seq_table[i].inuse) { 106762306a36Sopenharmony_ci intf->seq_table[i].recv_msg = recv_msg; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* 107062306a36Sopenharmony_ci * Start with the maximum timeout, when the send response 107162306a36Sopenharmony_ci * comes in we will start the real timer. 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ci intf->seq_table[i].timeout = MAX_MSG_TIMEOUT; 107462306a36Sopenharmony_ci intf->seq_table[i].orig_timeout = timeout; 107562306a36Sopenharmony_ci intf->seq_table[i].retries_left = retries; 107662306a36Sopenharmony_ci intf->seq_table[i].broadcast = broadcast; 107762306a36Sopenharmony_ci intf->seq_table[i].inuse = 1; 107862306a36Sopenharmony_ci intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid); 107962306a36Sopenharmony_ci *seq = i; 108062306a36Sopenharmony_ci *seqid = intf->seq_table[i].seqid; 108162306a36Sopenharmony_ci intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ; 108262306a36Sopenharmony_ci smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 108362306a36Sopenharmony_ci need_waiter(intf); 108462306a36Sopenharmony_ci } else { 108562306a36Sopenharmony_ci rv = -EAGAIN; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci return rv; 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci/* 109262306a36Sopenharmony_ci * Return the receive message for the given sequence number and 109362306a36Sopenharmony_ci * release the sequence number so it can be reused. Some other data 109462306a36Sopenharmony_ci * is passed in to be sure the message matches up correctly (to help 109562306a36Sopenharmony_ci * guard against message coming in after their timeout and the 109662306a36Sopenharmony_ci * sequence number being reused). 109762306a36Sopenharmony_ci */ 109862306a36Sopenharmony_cistatic int intf_find_seq(struct ipmi_smi *intf, 109962306a36Sopenharmony_ci unsigned char seq, 110062306a36Sopenharmony_ci short channel, 110162306a36Sopenharmony_ci unsigned char cmd, 110262306a36Sopenharmony_ci unsigned char netfn, 110362306a36Sopenharmony_ci struct ipmi_addr *addr, 110462306a36Sopenharmony_ci struct ipmi_recv_msg **recv_msg) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci int rv = -ENODEV; 110762306a36Sopenharmony_ci unsigned long flags; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (seq >= IPMI_IPMB_NUM_SEQ) 111062306a36Sopenharmony_ci return -EINVAL; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 111362306a36Sopenharmony_ci if (intf->seq_table[seq].inuse) { 111462306a36Sopenharmony_ci struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd) 111762306a36Sopenharmony_ci && (msg->msg.netfn == netfn) 111862306a36Sopenharmony_ci && (ipmi_addr_equal(addr, &msg->addr))) { 111962306a36Sopenharmony_ci *recv_msg = msg; 112062306a36Sopenharmony_ci intf->seq_table[seq].inuse = 0; 112162306a36Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 112262306a36Sopenharmony_ci rv = 0; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci return rv; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci/* Start the timer for a specific sequence table entry. */ 113262306a36Sopenharmony_cistatic int intf_start_seq_timer(struct ipmi_smi *intf, 113362306a36Sopenharmony_ci long msgid) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci int rv = -ENODEV; 113662306a36Sopenharmony_ci unsigned long flags; 113762306a36Sopenharmony_ci unsigned char seq; 113862306a36Sopenharmony_ci unsigned long seqid; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci GET_SEQ_FROM_MSGID(msgid, seq, seqid); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 114462306a36Sopenharmony_ci /* 114562306a36Sopenharmony_ci * We do this verification because the user can be deleted 114662306a36Sopenharmony_ci * while a message is outstanding. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci if ((intf->seq_table[seq].inuse) 114962306a36Sopenharmony_ci && (intf->seq_table[seq].seqid == seqid)) { 115062306a36Sopenharmony_ci struct seq_table *ent = &intf->seq_table[seq]; 115162306a36Sopenharmony_ci ent->timeout = ent->orig_timeout; 115262306a36Sopenharmony_ci rv = 0; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci return rv; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci/* Got an error for the send message for a specific sequence number. */ 116062306a36Sopenharmony_cistatic int intf_err_seq(struct ipmi_smi *intf, 116162306a36Sopenharmony_ci long msgid, 116262306a36Sopenharmony_ci unsigned int err) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci int rv = -ENODEV; 116562306a36Sopenharmony_ci unsigned long flags; 116662306a36Sopenharmony_ci unsigned char seq; 116762306a36Sopenharmony_ci unsigned long seqid; 116862306a36Sopenharmony_ci struct ipmi_recv_msg *msg = NULL; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci GET_SEQ_FROM_MSGID(msgid, seq, seqid); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 117462306a36Sopenharmony_ci /* 117562306a36Sopenharmony_ci * We do this verification because the user can be deleted 117662306a36Sopenharmony_ci * while a message is outstanding. 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_ci if ((intf->seq_table[seq].inuse) 117962306a36Sopenharmony_ci && (intf->seq_table[seq].seqid == seqid)) { 118062306a36Sopenharmony_ci struct seq_table *ent = &intf->seq_table[seq]; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci ent->inuse = 0; 118362306a36Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 118462306a36Sopenharmony_ci msg = ent->recv_msg; 118562306a36Sopenharmony_ci rv = 0; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (msg) 119062306a36Sopenharmony_ci deliver_err_response(intf, msg, err); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci return rv; 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic void free_user_work(struct work_struct *work) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct ipmi_user *user = container_of(work, struct ipmi_user, 119862306a36Sopenharmony_ci remove_work); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci cleanup_srcu_struct(&user->release_barrier); 120162306a36Sopenharmony_ci vfree(user); 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ciint ipmi_create_user(unsigned int if_num, 120562306a36Sopenharmony_ci const struct ipmi_user_hndl *handler, 120662306a36Sopenharmony_ci void *handler_data, 120762306a36Sopenharmony_ci struct ipmi_user **user) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci unsigned long flags; 121062306a36Sopenharmony_ci struct ipmi_user *new_user; 121162306a36Sopenharmony_ci int rv, index; 121262306a36Sopenharmony_ci struct ipmi_smi *intf; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* 121562306a36Sopenharmony_ci * There is no module usecount here, because it's not 121662306a36Sopenharmony_ci * required. Since this can only be used by and called from 121762306a36Sopenharmony_ci * other modules, they will implicitly use this module, and 121862306a36Sopenharmony_ci * thus this can't be removed unless the other modules are 121962306a36Sopenharmony_ci * removed. 122062306a36Sopenharmony_ci */ 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (handler == NULL) 122362306a36Sopenharmony_ci return -EINVAL; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* 122662306a36Sopenharmony_ci * Make sure the driver is actually initialized, this handles 122762306a36Sopenharmony_ci * problems with initialization order. 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ci rv = ipmi_init_msghandler(); 123062306a36Sopenharmony_ci if (rv) 123162306a36Sopenharmony_ci return rv; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci new_user = vzalloc(sizeof(*new_user)); 123462306a36Sopenharmony_ci if (!new_user) 123562306a36Sopenharmony_ci return -ENOMEM; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci index = srcu_read_lock(&ipmi_interfaces_srcu); 123862306a36Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 123962306a36Sopenharmony_ci if (intf->intf_num == if_num) 124062306a36Sopenharmony_ci goto found; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci /* Not found, return an error */ 124362306a36Sopenharmony_ci rv = -EINVAL; 124462306a36Sopenharmony_ci goto out_kfree; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci found: 124762306a36Sopenharmony_ci if (atomic_add_return(1, &intf->nr_users) > max_users) { 124862306a36Sopenharmony_ci rv = -EBUSY; 124962306a36Sopenharmony_ci goto out_kfree; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci INIT_WORK(&new_user->remove_work, free_user_work); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci rv = init_srcu_struct(&new_user->release_barrier); 125562306a36Sopenharmony_ci if (rv) 125662306a36Sopenharmony_ci goto out_kfree; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (!try_module_get(intf->owner)) { 125962306a36Sopenharmony_ci rv = -ENODEV; 126062306a36Sopenharmony_ci goto out_kfree; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci /* Note that each existing user holds a refcount to the interface. */ 126462306a36Sopenharmony_ci kref_get(&intf->refcount); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci atomic_set(&new_user->nr_msgs, 0); 126762306a36Sopenharmony_ci kref_init(&new_user->refcount); 126862306a36Sopenharmony_ci new_user->handler = handler; 126962306a36Sopenharmony_ci new_user->handler_data = handler_data; 127062306a36Sopenharmony_ci new_user->intf = intf; 127162306a36Sopenharmony_ci new_user->gets_events = false; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci rcu_assign_pointer(new_user->self, new_user); 127462306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 127562306a36Sopenharmony_ci list_add_rcu(&new_user->link, &intf->users); 127662306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 127762306a36Sopenharmony_ci if (handler->ipmi_watchdog_pretimeout) 127862306a36Sopenharmony_ci /* User wants pretimeouts, so make sure to watch for them. */ 127962306a36Sopenharmony_ci smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG); 128062306a36Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 128162306a36Sopenharmony_ci *user = new_user; 128262306a36Sopenharmony_ci return 0; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ciout_kfree: 128562306a36Sopenharmony_ci atomic_dec(&intf->nr_users); 128662306a36Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 128762306a36Sopenharmony_ci vfree(new_user); 128862306a36Sopenharmony_ci return rv; 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_create_user); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ciint ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci int rv, index; 129562306a36Sopenharmony_ci struct ipmi_smi *intf; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci index = srcu_read_lock(&ipmi_interfaces_srcu); 129862306a36Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 129962306a36Sopenharmony_ci if (intf->intf_num == if_num) 130062306a36Sopenharmony_ci goto found; 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci /* Not found, return an error */ 130562306a36Sopenharmony_ci return -EINVAL; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cifound: 130862306a36Sopenharmony_ci if (!intf->handlers->get_smi_info) 130962306a36Sopenharmony_ci rv = -ENOTTY; 131062306a36Sopenharmony_ci else 131162306a36Sopenharmony_ci rv = intf->handlers->get_smi_info(intf->send_info, data); 131262306a36Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci return rv; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_smi_info); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic void free_user(struct kref *ref) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci /* SRCU cleanup must happen in task context. */ 132362306a36Sopenharmony_ci queue_work(remove_work_wq, &user->remove_work); 132462306a36Sopenharmony_ci} 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistatic void _ipmi_destroy_user(struct ipmi_user *user) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci struct ipmi_smi *intf = user->intf; 132962306a36Sopenharmony_ci int i; 133062306a36Sopenharmony_ci unsigned long flags; 133162306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 133262306a36Sopenharmony_ci struct cmd_rcvr *rcvrs = NULL; 133362306a36Sopenharmony_ci struct module *owner; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci if (!acquire_ipmi_user(user, &i)) { 133662306a36Sopenharmony_ci /* 133762306a36Sopenharmony_ci * The user has already been cleaned up, just make sure 133862306a36Sopenharmony_ci * nothing is using it and return. 133962306a36Sopenharmony_ci */ 134062306a36Sopenharmony_ci synchronize_srcu(&user->release_barrier); 134162306a36Sopenharmony_ci return; 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci rcu_assign_pointer(user->self, NULL); 134562306a36Sopenharmony_ci release_ipmi_user(user, i); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci synchronize_srcu(&user->release_barrier); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (user->handler->shutdown) 135062306a36Sopenharmony_ci user->handler->shutdown(user->handler_data); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (user->handler->ipmi_watchdog_pretimeout) 135362306a36Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (user->gets_events) 135662306a36Sopenharmony_ci atomic_dec(&intf->event_waiters); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* Remove the user from the interface's sequence table. */ 135962306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 136062306a36Sopenharmony_ci list_del_rcu(&user->link); 136162306a36Sopenharmony_ci atomic_dec(&intf->nr_users); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { 136462306a36Sopenharmony_ci if (intf->seq_table[i].inuse 136562306a36Sopenharmony_ci && (intf->seq_table[i].recv_msg->user == user)) { 136662306a36Sopenharmony_ci intf->seq_table[i].inuse = 0; 136762306a36Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 136862306a36Sopenharmony_ci ipmi_free_recv_msg(intf->seq_table[i].recv_msg); 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci /* 137462306a36Sopenharmony_ci * Remove the user from the command receiver's table. First 137562306a36Sopenharmony_ci * we build a list of everything (not using the standard link, 137662306a36Sopenharmony_ci * since other things may be using it till we do 137762306a36Sopenharmony_ci * synchronize_srcu()) then free everything in that list. 137862306a36Sopenharmony_ci */ 137962306a36Sopenharmony_ci mutex_lock(&intf->cmd_rcvrs_mutex); 138062306a36Sopenharmony_ci list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link, 138162306a36Sopenharmony_ci lockdep_is_held(&intf->cmd_rcvrs_mutex)) { 138262306a36Sopenharmony_ci if (rcvr->user == user) { 138362306a36Sopenharmony_ci list_del_rcu(&rcvr->link); 138462306a36Sopenharmony_ci rcvr->next = rcvrs; 138562306a36Sopenharmony_ci rcvrs = rcvr; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci mutex_unlock(&intf->cmd_rcvrs_mutex); 138962306a36Sopenharmony_ci synchronize_rcu(); 139062306a36Sopenharmony_ci while (rcvrs) { 139162306a36Sopenharmony_ci rcvr = rcvrs; 139262306a36Sopenharmony_ci rcvrs = rcvr->next; 139362306a36Sopenharmony_ci kfree(rcvr); 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci owner = intf->owner; 139762306a36Sopenharmony_ci kref_put(&intf->refcount, intf_free); 139862306a36Sopenharmony_ci module_put(owner); 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ciint ipmi_destroy_user(struct ipmi_user *user) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci _ipmi_destroy_user(user); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci kref_put(&user->refcount, free_user); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci return 0; 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_destroy_user); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ciint ipmi_get_version(struct ipmi_user *user, 141262306a36Sopenharmony_ci unsigned char *major, 141362306a36Sopenharmony_ci unsigned char *minor) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci struct ipmi_device_id id; 141662306a36Sopenharmony_ci int rv, index; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 141962306a36Sopenharmony_ci if (!user) 142062306a36Sopenharmony_ci return -ENODEV; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL); 142362306a36Sopenharmony_ci if (!rv) { 142462306a36Sopenharmony_ci *major = ipmi_version_major(&id); 142562306a36Sopenharmony_ci *minor = ipmi_version_minor(&id); 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci release_ipmi_user(user, index); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci return rv; 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_version); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ciint ipmi_set_my_address(struct ipmi_user *user, 143462306a36Sopenharmony_ci unsigned int channel, 143562306a36Sopenharmony_ci unsigned char address) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci int index, rv = 0; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 144062306a36Sopenharmony_ci if (!user) 144162306a36Sopenharmony_ci return -ENODEV; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci if (channel >= IPMI_MAX_CHANNELS) { 144462306a36Sopenharmony_ci rv = -EINVAL; 144562306a36Sopenharmony_ci } else { 144662306a36Sopenharmony_ci channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); 144762306a36Sopenharmony_ci user->intf->addrinfo[channel].address = address; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci release_ipmi_user(user, index); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return rv; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_my_address); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ciint ipmi_get_my_address(struct ipmi_user *user, 145662306a36Sopenharmony_ci unsigned int channel, 145762306a36Sopenharmony_ci unsigned char *address) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci int index, rv = 0; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 146262306a36Sopenharmony_ci if (!user) 146362306a36Sopenharmony_ci return -ENODEV; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (channel >= IPMI_MAX_CHANNELS) { 146662306a36Sopenharmony_ci rv = -EINVAL; 146762306a36Sopenharmony_ci } else { 146862306a36Sopenharmony_ci channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); 146962306a36Sopenharmony_ci *address = user->intf->addrinfo[channel].address; 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci release_ipmi_user(user, index); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci return rv; 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_my_address); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ciint ipmi_set_my_LUN(struct ipmi_user *user, 147862306a36Sopenharmony_ci unsigned int channel, 147962306a36Sopenharmony_ci unsigned char LUN) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci int index, rv = 0; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 148462306a36Sopenharmony_ci if (!user) 148562306a36Sopenharmony_ci return -ENODEV; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (channel >= IPMI_MAX_CHANNELS) { 148862306a36Sopenharmony_ci rv = -EINVAL; 148962306a36Sopenharmony_ci } else { 149062306a36Sopenharmony_ci channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); 149162306a36Sopenharmony_ci user->intf->addrinfo[channel].lun = LUN & 0x3; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci release_ipmi_user(user, index); 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci return rv; 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_my_LUN); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ciint ipmi_get_my_LUN(struct ipmi_user *user, 150062306a36Sopenharmony_ci unsigned int channel, 150162306a36Sopenharmony_ci unsigned char *address) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci int index, rv = 0; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 150662306a36Sopenharmony_ci if (!user) 150762306a36Sopenharmony_ci return -ENODEV; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci if (channel >= IPMI_MAX_CHANNELS) { 151062306a36Sopenharmony_ci rv = -EINVAL; 151162306a36Sopenharmony_ci } else { 151262306a36Sopenharmony_ci channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); 151362306a36Sopenharmony_ci *address = user->intf->addrinfo[channel].lun; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci release_ipmi_user(user, index); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci return rv; 151862306a36Sopenharmony_ci} 151962306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_my_LUN); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ciint ipmi_get_maintenance_mode(struct ipmi_user *user) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci int mode, index; 152462306a36Sopenharmony_ci unsigned long flags; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 152762306a36Sopenharmony_ci if (!user) 152862306a36Sopenharmony_ci return -ENODEV; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags); 153162306a36Sopenharmony_ci mode = user->intf->maintenance_mode; 153262306a36Sopenharmony_ci spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags); 153362306a36Sopenharmony_ci release_ipmi_user(user, index); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci return mode; 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_maintenance_mode); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_cistatic void maintenance_mode_update(struct ipmi_smi *intf) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci if (intf->handlers->set_maintenance_mode) 154262306a36Sopenharmony_ci intf->handlers->set_maintenance_mode( 154362306a36Sopenharmony_ci intf->send_info, intf->maintenance_mode_enable); 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ciint ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci int rv = 0, index; 154962306a36Sopenharmony_ci unsigned long flags; 155062306a36Sopenharmony_ci struct ipmi_smi *intf = user->intf; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 155362306a36Sopenharmony_ci if (!user) 155462306a36Sopenharmony_ci return -ENODEV; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci spin_lock_irqsave(&intf->maintenance_mode_lock, flags); 155762306a36Sopenharmony_ci if (intf->maintenance_mode != mode) { 155862306a36Sopenharmony_ci switch (mode) { 155962306a36Sopenharmony_ci case IPMI_MAINTENANCE_MODE_AUTO: 156062306a36Sopenharmony_ci intf->maintenance_mode_enable 156162306a36Sopenharmony_ci = (intf->auto_maintenance_timeout > 0); 156262306a36Sopenharmony_ci break; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci case IPMI_MAINTENANCE_MODE_OFF: 156562306a36Sopenharmony_ci intf->maintenance_mode_enable = false; 156662306a36Sopenharmony_ci break; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci case IPMI_MAINTENANCE_MODE_ON: 156962306a36Sopenharmony_ci intf->maintenance_mode_enable = true; 157062306a36Sopenharmony_ci break; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci default: 157362306a36Sopenharmony_ci rv = -EINVAL; 157462306a36Sopenharmony_ci goto out_unlock; 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci intf->maintenance_mode = mode; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci maintenance_mode_update(intf); 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci out_unlock: 158162306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); 158262306a36Sopenharmony_ci release_ipmi_user(user, index); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci return rv; 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_maintenance_mode); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ciint ipmi_set_gets_events(struct ipmi_user *user, bool val) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci unsigned long flags; 159162306a36Sopenharmony_ci struct ipmi_smi *intf = user->intf; 159262306a36Sopenharmony_ci struct ipmi_recv_msg *msg, *msg2; 159362306a36Sopenharmony_ci struct list_head msgs; 159462306a36Sopenharmony_ci int index; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 159762306a36Sopenharmony_ci if (!user) 159862306a36Sopenharmony_ci return -ENODEV; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci INIT_LIST_HEAD(&msgs); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci spin_lock_irqsave(&intf->events_lock, flags); 160362306a36Sopenharmony_ci if (user->gets_events == val) 160462306a36Sopenharmony_ci goto out; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci user->gets_events = val; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci if (val) { 160962306a36Sopenharmony_ci if (atomic_inc_return(&intf->event_waiters) == 1) 161062306a36Sopenharmony_ci need_waiter(intf); 161162306a36Sopenharmony_ci } else { 161262306a36Sopenharmony_ci atomic_dec(&intf->event_waiters); 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci if (intf->delivering_events) 161662306a36Sopenharmony_ci /* 161762306a36Sopenharmony_ci * Another thread is delivering events for this, so 161862306a36Sopenharmony_ci * let it handle any new events. 161962306a36Sopenharmony_ci */ 162062306a36Sopenharmony_ci goto out; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci /* Deliver any queued events. */ 162362306a36Sopenharmony_ci while (user->gets_events && !list_empty(&intf->waiting_events)) { 162462306a36Sopenharmony_ci list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) 162562306a36Sopenharmony_ci list_move_tail(&msg->link, &msgs); 162662306a36Sopenharmony_ci intf->waiting_events_count = 0; 162762306a36Sopenharmony_ci if (intf->event_msg_printed) { 162862306a36Sopenharmony_ci dev_warn(intf->si_dev, "Event queue no longer full\n"); 162962306a36Sopenharmony_ci intf->event_msg_printed = 0; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci intf->delivering_events = 1; 163362306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->events_lock, flags); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci list_for_each_entry_safe(msg, msg2, &msgs, link) { 163662306a36Sopenharmony_ci msg->user = user; 163762306a36Sopenharmony_ci kref_get(&user->refcount); 163862306a36Sopenharmony_ci deliver_local_response(intf, msg); 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci spin_lock_irqsave(&intf->events_lock, flags); 164262306a36Sopenharmony_ci intf->delivering_events = 0; 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci out: 164662306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->events_lock, flags); 164762306a36Sopenharmony_ci release_ipmi_user(user, index); 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci return 0; 165062306a36Sopenharmony_ci} 165162306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_gets_events); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_cistatic struct cmd_rcvr *find_cmd_rcvr(struct ipmi_smi *intf, 165462306a36Sopenharmony_ci unsigned char netfn, 165562306a36Sopenharmony_ci unsigned char cmd, 165662306a36Sopenharmony_ci unsigned char chan) 165762306a36Sopenharmony_ci{ 165862306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link, 166162306a36Sopenharmony_ci lockdep_is_held(&intf->cmd_rcvrs_mutex)) { 166262306a36Sopenharmony_ci if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) 166362306a36Sopenharmony_ci && (rcvr->chans & (1 << chan))) 166462306a36Sopenharmony_ci return rcvr; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci return NULL; 166762306a36Sopenharmony_ci} 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cistatic int is_cmd_rcvr_exclusive(struct ipmi_smi *intf, 167062306a36Sopenharmony_ci unsigned char netfn, 167162306a36Sopenharmony_ci unsigned char cmd, 167262306a36Sopenharmony_ci unsigned int chans) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link, 167762306a36Sopenharmony_ci lockdep_is_held(&intf->cmd_rcvrs_mutex)) { 167862306a36Sopenharmony_ci if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) 167962306a36Sopenharmony_ci && (rcvr->chans & chans)) 168062306a36Sopenharmony_ci return 0; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci return 1; 168362306a36Sopenharmony_ci} 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ciint ipmi_register_for_cmd(struct ipmi_user *user, 168662306a36Sopenharmony_ci unsigned char netfn, 168762306a36Sopenharmony_ci unsigned char cmd, 168862306a36Sopenharmony_ci unsigned int chans) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci struct ipmi_smi *intf = user->intf; 169162306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 169262306a36Sopenharmony_ci int rv = 0, index; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 169562306a36Sopenharmony_ci if (!user) 169662306a36Sopenharmony_ci return -ENODEV; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL); 169962306a36Sopenharmony_ci if (!rcvr) { 170062306a36Sopenharmony_ci rv = -ENOMEM; 170162306a36Sopenharmony_ci goto out_release; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci rcvr->cmd = cmd; 170462306a36Sopenharmony_ci rcvr->netfn = netfn; 170562306a36Sopenharmony_ci rcvr->chans = chans; 170662306a36Sopenharmony_ci rcvr->user = user; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci mutex_lock(&intf->cmd_rcvrs_mutex); 170962306a36Sopenharmony_ci /* Make sure the command/netfn is not already registered. */ 171062306a36Sopenharmony_ci if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) { 171162306a36Sopenharmony_ci rv = -EBUSY; 171262306a36Sopenharmony_ci goto out_unlock; 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ciout_unlock: 172062306a36Sopenharmony_ci mutex_unlock(&intf->cmd_rcvrs_mutex); 172162306a36Sopenharmony_ci if (rv) 172262306a36Sopenharmony_ci kfree(rcvr); 172362306a36Sopenharmony_ciout_release: 172462306a36Sopenharmony_ci release_ipmi_user(user, index); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci return rv; 172762306a36Sopenharmony_ci} 172862306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_register_for_cmd); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ciint ipmi_unregister_for_cmd(struct ipmi_user *user, 173162306a36Sopenharmony_ci unsigned char netfn, 173262306a36Sopenharmony_ci unsigned char cmd, 173362306a36Sopenharmony_ci unsigned int chans) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci struct ipmi_smi *intf = user->intf; 173662306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 173762306a36Sopenharmony_ci struct cmd_rcvr *rcvrs = NULL; 173862306a36Sopenharmony_ci int i, rv = -ENOENT, index; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 174162306a36Sopenharmony_ci if (!user) 174262306a36Sopenharmony_ci return -ENODEV; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci mutex_lock(&intf->cmd_rcvrs_mutex); 174562306a36Sopenharmony_ci for (i = 0; i < IPMI_NUM_CHANNELS; i++) { 174662306a36Sopenharmony_ci if (((1 << i) & chans) == 0) 174762306a36Sopenharmony_ci continue; 174862306a36Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, i); 174962306a36Sopenharmony_ci if (rcvr == NULL) 175062306a36Sopenharmony_ci continue; 175162306a36Sopenharmony_ci if (rcvr->user == user) { 175262306a36Sopenharmony_ci rv = 0; 175362306a36Sopenharmony_ci rcvr->chans &= ~chans; 175462306a36Sopenharmony_ci if (rcvr->chans == 0) { 175562306a36Sopenharmony_ci list_del_rcu(&rcvr->link); 175662306a36Sopenharmony_ci rcvr->next = rcvrs; 175762306a36Sopenharmony_ci rcvrs = rcvr; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci mutex_unlock(&intf->cmd_rcvrs_mutex); 176262306a36Sopenharmony_ci synchronize_rcu(); 176362306a36Sopenharmony_ci release_ipmi_user(user, index); 176462306a36Sopenharmony_ci while (rcvrs) { 176562306a36Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS); 176662306a36Sopenharmony_ci rcvr = rcvrs; 176762306a36Sopenharmony_ci rcvrs = rcvr->next; 176862306a36Sopenharmony_ci kfree(rcvr); 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci return rv; 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_unregister_for_cmd); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ciunsigned char 177662306a36Sopenharmony_ciipmb_checksum(unsigned char *data, int size) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci unsigned char csum = 0; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci for (; size > 0; size--, data++) 178162306a36Sopenharmony_ci csum += *data; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci return -csum; 178462306a36Sopenharmony_ci} 178562306a36Sopenharmony_ciEXPORT_SYMBOL(ipmb_checksum); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_cistatic inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg, 178862306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 178962306a36Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr, 179062306a36Sopenharmony_ci long msgid, 179162306a36Sopenharmony_ci unsigned char ipmb_seq, 179262306a36Sopenharmony_ci int broadcast, 179362306a36Sopenharmony_ci unsigned char source_address, 179462306a36Sopenharmony_ci unsigned char source_lun) 179562306a36Sopenharmony_ci{ 179662306a36Sopenharmony_ci int i = broadcast; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci /* Format the IPMB header data. */ 179962306a36Sopenharmony_ci smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 180062306a36Sopenharmony_ci smi_msg->data[1] = IPMI_SEND_MSG_CMD; 180162306a36Sopenharmony_ci smi_msg->data[2] = ipmb_addr->channel; 180262306a36Sopenharmony_ci if (broadcast) 180362306a36Sopenharmony_ci smi_msg->data[3] = 0; 180462306a36Sopenharmony_ci smi_msg->data[i+3] = ipmb_addr->slave_addr; 180562306a36Sopenharmony_ci smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3); 180662306a36Sopenharmony_ci smi_msg->data[i+5] = ipmb_checksum(&smi_msg->data[i + 3], 2); 180762306a36Sopenharmony_ci smi_msg->data[i+6] = source_address; 180862306a36Sopenharmony_ci smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun; 180962306a36Sopenharmony_ci smi_msg->data[i+8] = msg->cmd; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci /* Now tack on the data to the message. */ 181262306a36Sopenharmony_ci if (msg->data_len > 0) 181362306a36Sopenharmony_ci memcpy(&smi_msg->data[i + 9], msg->data, msg->data_len); 181462306a36Sopenharmony_ci smi_msg->data_size = msg->data_len + 9; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci /* Now calculate the checksum and tack it on. */ 181762306a36Sopenharmony_ci smi_msg->data[i+smi_msg->data_size] 181862306a36Sopenharmony_ci = ipmb_checksum(&smi_msg->data[i + 6], smi_msg->data_size - 6); 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci /* 182162306a36Sopenharmony_ci * Add on the checksum size and the offset from the 182262306a36Sopenharmony_ci * broadcast. 182362306a36Sopenharmony_ci */ 182462306a36Sopenharmony_ci smi_msg->data_size += 1 + i; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci smi_msg->msgid = msgid; 182762306a36Sopenharmony_ci} 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic inline void format_lan_msg(struct ipmi_smi_msg *smi_msg, 183062306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 183162306a36Sopenharmony_ci struct ipmi_lan_addr *lan_addr, 183262306a36Sopenharmony_ci long msgid, 183362306a36Sopenharmony_ci unsigned char ipmb_seq, 183462306a36Sopenharmony_ci unsigned char source_lun) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci /* Format the IPMB header data. */ 183762306a36Sopenharmony_ci smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 183862306a36Sopenharmony_ci smi_msg->data[1] = IPMI_SEND_MSG_CMD; 183962306a36Sopenharmony_ci smi_msg->data[2] = lan_addr->channel; 184062306a36Sopenharmony_ci smi_msg->data[3] = lan_addr->session_handle; 184162306a36Sopenharmony_ci smi_msg->data[4] = lan_addr->remote_SWID; 184262306a36Sopenharmony_ci smi_msg->data[5] = (msg->netfn << 2) | (lan_addr->lun & 0x3); 184362306a36Sopenharmony_ci smi_msg->data[6] = ipmb_checksum(&smi_msg->data[4], 2); 184462306a36Sopenharmony_ci smi_msg->data[7] = lan_addr->local_SWID; 184562306a36Sopenharmony_ci smi_msg->data[8] = (ipmb_seq << 2) | source_lun; 184662306a36Sopenharmony_ci smi_msg->data[9] = msg->cmd; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci /* Now tack on the data to the message. */ 184962306a36Sopenharmony_ci if (msg->data_len > 0) 185062306a36Sopenharmony_ci memcpy(&smi_msg->data[10], msg->data, msg->data_len); 185162306a36Sopenharmony_ci smi_msg->data_size = msg->data_len + 10; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci /* Now calculate the checksum and tack it on. */ 185462306a36Sopenharmony_ci smi_msg->data[smi_msg->data_size] 185562306a36Sopenharmony_ci = ipmb_checksum(&smi_msg->data[7], smi_msg->data_size - 7); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci /* 185862306a36Sopenharmony_ci * Add on the checksum size and the offset from the 185962306a36Sopenharmony_ci * broadcast. 186062306a36Sopenharmony_ci */ 186162306a36Sopenharmony_ci smi_msg->data_size += 1; 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci smi_msg->msgid = msgid; 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf, 186762306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 186862306a36Sopenharmony_ci int priority) 186962306a36Sopenharmony_ci{ 187062306a36Sopenharmony_ci if (intf->curr_msg) { 187162306a36Sopenharmony_ci if (priority > 0) 187262306a36Sopenharmony_ci list_add_tail(&smi_msg->link, &intf->hp_xmit_msgs); 187362306a36Sopenharmony_ci else 187462306a36Sopenharmony_ci list_add_tail(&smi_msg->link, &intf->xmit_msgs); 187562306a36Sopenharmony_ci smi_msg = NULL; 187662306a36Sopenharmony_ci } else { 187762306a36Sopenharmony_ci intf->curr_msg = smi_msg; 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci return smi_msg; 188162306a36Sopenharmony_ci} 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_cistatic void smi_send(struct ipmi_smi *intf, 188462306a36Sopenharmony_ci const struct ipmi_smi_handlers *handlers, 188562306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg, int priority) 188662306a36Sopenharmony_ci{ 188762306a36Sopenharmony_ci int run_to_completion = intf->run_to_completion; 188862306a36Sopenharmony_ci unsigned long flags = 0; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci if (!run_to_completion) 189162306a36Sopenharmony_ci spin_lock_irqsave(&intf->xmit_msgs_lock, flags); 189262306a36Sopenharmony_ci smi_msg = smi_add_send_msg(intf, smi_msg, priority); 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci if (!run_to_completion) 189562306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci if (smi_msg) 189862306a36Sopenharmony_ci handlers->sender(intf->send_info, smi_msg); 189962306a36Sopenharmony_ci} 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_cistatic bool is_maintenance_mode_cmd(struct kernel_ipmi_msg *msg) 190262306a36Sopenharmony_ci{ 190362306a36Sopenharmony_ci return (((msg->netfn == IPMI_NETFN_APP_REQUEST) 190462306a36Sopenharmony_ci && ((msg->cmd == IPMI_COLD_RESET_CMD) 190562306a36Sopenharmony_ci || (msg->cmd == IPMI_WARM_RESET_CMD))) 190662306a36Sopenharmony_ci || (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST)); 190762306a36Sopenharmony_ci} 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_cistatic int i_ipmi_req_sysintf(struct ipmi_smi *intf, 191062306a36Sopenharmony_ci struct ipmi_addr *addr, 191162306a36Sopenharmony_ci long msgid, 191262306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 191362306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 191462306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 191562306a36Sopenharmony_ci int retries, 191662306a36Sopenharmony_ci unsigned int retry_time_ms) 191762306a36Sopenharmony_ci{ 191862306a36Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci if (msg->netfn & 1) 192162306a36Sopenharmony_ci /* Responses are not allowed to the SMI. */ 192262306a36Sopenharmony_ci return -EINVAL; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci smi_addr = (struct ipmi_system_interface_addr *) addr; 192562306a36Sopenharmony_ci if (smi_addr->lun > 3) { 192662306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 192762306a36Sopenharmony_ci return -EINVAL; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr)); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci if ((msg->netfn == IPMI_NETFN_APP_REQUEST) 193362306a36Sopenharmony_ci && ((msg->cmd == IPMI_SEND_MSG_CMD) 193462306a36Sopenharmony_ci || (msg->cmd == IPMI_GET_MSG_CMD) 193562306a36Sopenharmony_ci || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) { 193662306a36Sopenharmony_ci /* 193762306a36Sopenharmony_ci * We don't let the user do these, since we manage 193862306a36Sopenharmony_ci * the sequence numbers. 193962306a36Sopenharmony_ci */ 194062306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 194162306a36Sopenharmony_ci return -EINVAL; 194262306a36Sopenharmony_ci } 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci if (is_maintenance_mode_cmd(msg)) { 194562306a36Sopenharmony_ci unsigned long flags; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci spin_lock_irqsave(&intf->maintenance_mode_lock, flags); 194862306a36Sopenharmony_ci intf->auto_maintenance_timeout 194962306a36Sopenharmony_ci = maintenance_mode_timeout_ms; 195062306a36Sopenharmony_ci if (!intf->maintenance_mode 195162306a36Sopenharmony_ci && !intf->maintenance_mode_enable) { 195262306a36Sopenharmony_ci intf->maintenance_mode_enable = true; 195362306a36Sopenharmony_ci maintenance_mode_update(intf); 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->maintenance_mode_lock, 195662306a36Sopenharmony_ci flags); 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci if (msg->data_len + 2 > IPMI_MAX_MSG_LENGTH) { 196062306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 196162306a36Sopenharmony_ci return -EMSGSIZE; 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3); 196562306a36Sopenharmony_ci smi_msg->data[1] = msg->cmd; 196662306a36Sopenharmony_ci smi_msg->msgid = msgid; 196762306a36Sopenharmony_ci smi_msg->user_data = recv_msg; 196862306a36Sopenharmony_ci if (msg->data_len > 0) 196962306a36Sopenharmony_ci memcpy(&smi_msg->data[2], msg->data, msg->data_len); 197062306a36Sopenharmony_ci smi_msg->data_size = msg->data_len + 2; 197162306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_local_commands); 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci return 0; 197462306a36Sopenharmony_ci} 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_cistatic int i_ipmi_req_ipmb(struct ipmi_smi *intf, 197762306a36Sopenharmony_ci struct ipmi_addr *addr, 197862306a36Sopenharmony_ci long msgid, 197962306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 198062306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 198162306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 198262306a36Sopenharmony_ci unsigned char source_address, 198362306a36Sopenharmony_ci unsigned char source_lun, 198462306a36Sopenharmony_ci int retries, 198562306a36Sopenharmony_ci unsigned int retry_time_ms) 198662306a36Sopenharmony_ci{ 198762306a36Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr; 198862306a36Sopenharmony_ci unsigned char ipmb_seq; 198962306a36Sopenharmony_ci long seqid; 199062306a36Sopenharmony_ci int broadcast = 0; 199162306a36Sopenharmony_ci struct ipmi_channel *chans; 199262306a36Sopenharmony_ci int rv = 0; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci if (addr->channel >= IPMI_MAX_CHANNELS) { 199562306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 199662306a36Sopenharmony_ci return -EINVAL; 199762306a36Sopenharmony_ci } 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci chans = READ_ONCE(intf->channel_list)->c; 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) { 200262306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 200362306a36Sopenharmony_ci return -EINVAL; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) { 200762306a36Sopenharmony_ci /* 200862306a36Sopenharmony_ci * Broadcasts add a zero at the beginning of the 200962306a36Sopenharmony_ci * message, but otherwise is the same as an IPMB 201062306a36Sopenharmony_ci * address. 201162306a36Sopenharmony_ci */ 201262306a36Sopenharmony_ci addr->addr_type = IPMI_IPMB_ADDR_TYPE; 201362306a36Sopenharmony_ci broadcast = 1; 201462306a36Sopenharmony_ci retries = 0; /* Don't retry broadcasts. */ 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci /* 201862306a36Sopenharmony_ci * 9 for the header and 1 for the checksum, plus 201962306a36Sopenharmony_ci * possibly one for the broadcast. 202062306a36Sopenharmony_ci */ 202162306a36Sopenharmony_ci if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) { 202262306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 202362306a36Sopenharmony_ci return -EMSGSIZE; 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci ipmb_addr = (struct ipmi_ipmb_addr *) addr; 202762306a36Sopenharmony_ci if (ipmb_addr->lun > 3) { 202862306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 202962306a36Sopenharmony_ci return -EINVAL; 203062306a36Sopenharmony_ci } 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr)); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci if (recv_msg->msg.netfn & 0x1) { 203562306a36Sopenharmony_ci /* 203662306a36Sopenharmony_ci * It's a response, so use the user's sequence 203762306a36Sopenharmony_ci * from msgid. 203862306a36Sopenharmony_ci */ 203962306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_ipmb_responses); 204062306a36Sopenharmony_ci format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid, 204162306a36Sopenharmony_ci msgid, broadcast, 204262306a36Sopenharmony_ci source_address, source_lun); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci /* 204562306a36Sopenharmony_ci * Save the receive message so we can use it 204662306a36Sopenharmony_ci * to deliver the response. 204762306a36Sopenharmony_ci */ 204862306a36Sopenharmony_ci smi_msg->user_data = recv_msg; 204962306a36Sopenharmony_ci } else { 205062306a36Sopenharmony_ci /* It's a command, so get a sequence for it. */ 205162306a36Sopenharmony_ci unsigned long flags; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci if (is_maintenance_mode_cmd(msg)) 205662306a36Sopenharmony_ci intf->ipmb_maintenance_mode_timeout = 205762306a36Sopenharmony_ci maintenance_mode_timeout_ms; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci if (intf->ipmb_maintenance_mode_timeout && retry_time_ms == 0) 206062306a36Sopenharmony_ci /* Different default in maintenance mode */ 206162306a36Sopenharmony_ci retry_time_ms = default_maintenance_retry_ms; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci /* 206462306a36Sopenharmony_ci * Create a sequence number with a 1 second 206562306a36Sopenharmony_ci * timeout and 4 retries. 206662306a36Sopenharmony_ci */ 206762306a36Sopenharmony_ci rv = intf_next_seq(intf, 206862306a36Sopenharmony_ci recv_msg, 206962306a36Sopenharmony_ci retry_time_ms, 207062306a36Sopenharmony_ci retries, 207162306a36Sopenharmony_ci broadcast, 207262306a36Sopenharmony_ci &ipmb_seq, 207362306a36Sopenharmony_ci &seqid); 207462306a36Sopenharmony_ci if (rv) 207562306a36Sopenharmony_ci /* 207662306a36Sopenharmony_ci * We have used up all the sequence numbers, 207762306a36Sopenharmony_ci * probably, so abort. 207862306a36Sopenharmony_ci */ 207962306a36Sopenharmony_ci goto out_err; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_ipmb_commands); 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci /* 208462306a36Sopenharmony_ci * Store the sequence number in the message, 208562306a36Sopenharmony_ci * so that when the send message response 208662306a36Sopenharmony_ci * comes back we can start the timer. 208762306a36Sopenharmony_ci */ 208862306a36Sopenharmony_ci format_ipmb_msg(smi_msg, msg, ipmb_addr, 208962306a36Sopenharmony_ci STORE_SEQ_IN_MSGID(ipmb_seq, seqid), 209062306a36Sopenharmony_ci ipmb_seq, broadcast, 209162306a36Sopenharmony_ci source_address, source_lun); 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci /* 209462306a36Sopenharmony_ci * Copy the message into the recv message data, so we 209562306a36Sopenharmony_ci * can retransmit it later if necessary. 209662306a36Sopenharmony_ci */ 209762306a36Sopenharmony_ci memcpy(recv_msg->msg_data, smi_msg->data, 209862306a36Sopenharmony_ci smi_msg->data_size); 209962306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 210062306a36Sopenharmony_ci recv_msg->msg.data_len = smi_msg->data_size; 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci /* 210362306a36Sopenharmony_ci * We don't unlock until here, because we need 210462306a36Sopenharmony_ci * to copy the completed message into the 210562306a36Sopenharmony_ci * recv_msg before we release the lock. 210662306a36Sopenharmony_ci * Otherwise, race conditions may bite us. I 210762306a36Sopenharmony_ci * know that's pretty paranoid, but I prefer 210862306a36Sopenharmony_ci * to be correct. 210962306a36Sopenharmony_ci */ 211062306a36Sopenharmony_ciout_err: 211162306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci return rv; 211562306a36Sopenharmony_ci} 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_cistatic int i_ipmi_req_ipmb_direct(struct ipmi_smi *intf, 211862306a36Sopenharmony_ci struct ipmi_addr *addr, 211962306a36Sopenharmony_ci long msgid, 212062306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 212162306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 212262306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 212362306a36Sopenharmony_ci unsigned char source_lun) 212462306a36Sopenharmony_ci{ 212562306a36Sopenharmony_ci struct ipmi_ipmb_direct_addr *daddr; 212662306a36Sopenharmony_ci bool is_cmd = !(recv_msg->msg.netfn & 0x1); 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci if (!(intf->handlers->flags & IPMI_SMI_CAN_HANDLE_IPMB_DIRECT)) 212962306a36Sopenharmony_ci return -EAFNOSUPPORT; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci /* Responses must have a completion code. */ 213262306a36Sopenharmony_ci if (!is_cmd && msg->data_len < 1) { 213362306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 213462306a36Sopenharmony_ci return -EINVAL; 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci if ((msg->data_len + 4) > IPMI_MAX_MSG_LENGTH) { 213862306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 213962306a36Sopenharmony_ci return -EMSGSIZE; 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci daddr = (struct ipmi_ipmb_direct_addr *) addr; 214362306a36Sopenharmony_ci if (daddr->rq_lun > 3 || daddr->rs_lun > 3) { 214462306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 214562306a36Sopenharmony_ci return -EINVAL; 214662306a36Sopenharmony_ci } 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci smi_msg->type = IPMI_SMI_MSG_TYPE_IPMB_DIRECT; 214962306a36Sopenharmony_ci smi_msg->msgid = msgid; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci if (is_cmd) { 215262306a36Sopenharmony_ci smi_msg->data[0] = msg->netfn << 2 | daddr->rs_lun; 215362306a36Sopenharmony_ci smi_msg->data[2] = recv_msg->msgid << 2 | daddr->rq_lun; 215462306a36Sopenharmony_ci } else { 215562306a36Sopenharmony_ci smi_msg->data[0] = msg->netfn << 2 | daddr->rq_lun; 215662306a36Sopenharmony_ci smi_msg->data[2] = recv_msg->msgid << 2 | daddr->rs_lun; 215762306a36Sopenharmony_ci } 215862306a36Sopenharmony_ci smi_msg->data[1] = daddr->slave_addr; 215962306a36Sopenharmony_ci smi_msg->data[3] = msg->cmd; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci memcpy(smi_msg->data + 4, msg->data, msg->data_len); 216262306a36Sopenharmony_ci smi_msg->data_size = msg->data_len + 4; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci smi_msg->user_data = recv_msg; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci return 0; 216762306a36Sopenharmony_ci} 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_cistatic int i_ipmi_req_lan(struct ipmi_smi *intf, 217062306a36Sopenharmony_ci struct ipmi_addr *addr, 217162306a36Sopenharmony_ci long msgid, 217262306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 217362306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 217462306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 217562306a36Sopenharmony_ci unsigned char source_lun, 217662306a36Sopenharmony_ci int retries, 217762306a36Sopenharmony_ci unsigned int retry_time_ms) 217862306a36Sopenharmony_ci{ 217962306a36Sopenharmony_ci struct ipmi_lan_addr *lan_addr; 218062306a36Sopenharmony_ci unsigned char ipmb_seq; 218162306a36Sopenharmony_ci long seqid; 218262306a36Sopenharmony_ci struct ipmi_channel *chans; 218362306a36Sopenharmony_ci int rv = 0; 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci if (addr->channel >= IPMI_MAX_CHANNELS) { 218662306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 218762306a36Sopenharmony_ci return -EINVAL; 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci chans = READ_ONCE(intf->channel_list)->c; 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci if ((chans[addr->channel].medium 219362306a36Sopenharmony_ci != IPMI_CHANNEL_MEDIUM_8023LAN) 219462306a36Sopenharmony_ci && (chans[addr->channel].medium 219562306a36Sopenharmony_ci != IPMI_CHANNEL_MEDIUM_ASYNC)) { 219662306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 219762306a36Sopenharmony_ci return -EINVAL; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci /* 11 for the header and 1 for the checksum. */ 220162306a36Sopenharmony_ci if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) { 220262306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 220362306a36Sopenharmony_ci return -EMSGSIZE; 220462306a36Sopenharmony_ci } 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci lan_addr = (struct ipmi_lan_addr *) addr; 220762306a36Sopenharmony_ci if (lan_addr->lun > 3) { 220862306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 220962306a36Sopenharmony_ci return -EINVAL; 221062306a36Sopenharmony_ci } 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr)); 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci if (recv_msg->msg.netfn & 0x1) { 221562306a36Sopenharmony_ci /* 221662306a36Sopenharmony_ci * It's a response, so use the user's sequence 221762306a36Sopenharmony_ci * from msgid. 221862306a36Sopenharmony_ci */ 221962306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_lan_responses); 222062306a36Sopenharmony_ci format_lan_msg(smi_msg, msg, lan_addr, msgid, 222162306a36Sopenharmony_ci msgid, source_lun); 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci /* 222462306a36Sopenharmony_ci * Save the receive message so we can use it 222562306a36Sopenharmony_ci * to deliver the response. 222662306a36Sopenharmony_ci */ 222762306a36Sopenharmony_ci smi_msg->user_data = recv_msg; 222862306a36Sopenharmony_ci } else { 222962306a36Sopenharmony_ci /* It's a command, so get a sequence for it. */ 223062306a36Sopenharmony_ci unsigned long flags; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci /* 223562306a36Sopenharmony_ci * Create a sequence number with a 1 second 223662306a36Sopenharmony_ci * timeout and 4 retries. 223762306a36Sopenharmony_ci */ 223862306a36Sopenharmony_ci rv = intf_next_seq(intf, 223962306a36Sopenharmony_ci recv_msg, 224062306a36Sopenharmony_ci retry_time_ms, 224162306a36Sopenharmony_ci retries, 224262306a36Sopenharmony_ci 0, 224362306a36Sopenharmony_ci &ipmb_seq, 224462306a36Sopenharmony_ci &seqid); 224562306a36Sopenharmony_ci if (rv) 224662306a36Sopenharmony_ci /* 224762306a36Sopenharmony_ci * We have used up all the sequence numbers, 224862306a36Sopenharmony_ci * probably, so abort. 224962306a36Sopenharmony_ci */ 225062306a36Sopenharmony_ci goto out_err; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_lan_commands); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci /* 225562306a36Sopenharmony_ci * Store the sequence number in the message, 225662306a36Sopenharmony_ci * so that when the send message response 225762306a36Sopenharmony_ci * comes back we can start the timer. 225862306a36Sopenharmony_ci */ 225962306a36Sopenharmony_ci format_lan_msg(smi_msg, msg, lan_addr, 226062306a36Sopenharmony_ci STORE_SEQ_IN_MSGID(ipmb_seq, seqid), 226162306a36Sopenharmony_ci ipmb_seq, source_lun); 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci /* 226462306a36Sopenharmony_ci * Copy the message into the recv message data, so we 226562306a36Sopenharmony_ci * can retransmit it later if necessary. 226662306a36Sopenharmony_ci */ 226762306a36Sopenharmony_ci memcpy(recv_msg->msg_data, smi_msg->data, 226862306a36Sopenharmony_ci smi_msg->data_size); 226962306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 227062306a36Sopenharmony_ci recv_msg->msg.data_len = smi_msg->data_size; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci /* 227362306a36Sopenharmony_ci * We don't unlock until here, because we need 227462306a36Sopenharmony_ci * to copy the completed message into the 227562306a36Sopenharmony_ci * recv_msg before we release the lock. 227662306a36Sopenharmony_ci * Otherwise, race conditions may bite us. I 227762306a36Sopenharmony_ci * know that's pretty paranoid, but I prefer 227862306a36Sopenharmony_ci * to be correct. 227962306a36Sopenharmony_ci */ 228062306a36Sopenharmony_ciout_err: 228162306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 228262306a36Sopenharmony_ci } 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci return rv; 228562306a36Sopenharmony_ci} 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci/* 228862306a36Sopenharmony_ci * Separate from ipmi_request so that the user does not have to be 228962306a36Sopenharmony_ci * supplied in certain circumstances (mainly at panic time). If 229062306a36Sopenharmony_ci * messages are supplied, they will be freed, even if an error 229162306a36Sopenharmony_ci * occurs. 229262306a36Sopenharmony_ci */ 229362306a36Sopenharmony_cistatic int i_ipmi_request(struct ipmi_user *user, 229462306a36Sopenharmony_ci struct ipmi_smi *intf, 229562306a36Sopenharmony_ci struct ipmi_addr *addr, 229662306a36Sopenharmony_ci long msgid, 229762306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 229862306a36Sopenharmony_ci void *user_msg_data, 229962306a36Sopenharmony_ci void *supplied_smi, 230062306a36Sopenharmony_ci struct ipmi_recv_msg *supplied_recv, 230162306a36Sopenharmony_ci int priority, 230262306a36Sopenharmony_ci unsigned char source_address, 230362306a36Sopenharmony_ci unsigned char source_lun, 230462306a36Sopenharmony_ci int retries, 230562306a36Sopenharmony_ci unsigned int retry_time_ms) 230662306a36Sopenharmony_ci{ 230762306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg; 230862306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 230962306a36Sopenharmony_ci int rv = 0; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci if (user) { 231262306a36Sopenharmony_ci if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) { 231362306a36Sopenharmony_ci /* Decrement will happen at the end of the routine. */ 231462306a36Sopenharmony_ci rv = -EBUSY; 231562306a36Sopenharmony_ci goto out; 231662306a36Sopenharmony_ci } 231762306a36Sopenharmony_ci } 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci if (supplied_recv) 232062306a36Sopenharmony_ci recv_msg = supplied_recv; 232162306a36Sopenharmony_ci else { 232262306a36Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 232362306a36Sopenharmony_ci if (recv_msg == NULL) { 232462306a36Sopenharmony_ci rv = -ENOMEM; 232562306a36Sopenharmony_ci goto out; 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci } 232862306a36Sopenharmony_ci recv_msg->user_msg_data = user_msg_data; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci if (supplied_smi) 233162306a36Sopenharmony_ci smi_msg = supplied_smi; 233262306a36Sopenharmony_ci else { 233362306a36Sopenharmony_ci smi_msg = ipmi_alloc_smi_msg(); 233462306a36Sopenharmony_ci if (smi_msg == NULL) { 233562306a36Sopenharmony_ci if (!supplied_recv) 233662306a36Sopenharmony_ci ipmi_free_recv_msg(recv_msg); 233762306a36Sopenharmony_ci rv = -ENOMEM; 233862306a36Sopenharmony_ci goto out; 233962306a36Sopenharmony_ci } 234062306a36Sopenharmony_ci } 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci rcu_read_lock(); 234362306a36Sopenharmony_ci if (intf->in_shutdown) { 234462306a36Sopenharmony_ci rv = -ENODEV; 234562306a36Sopenharmony_ci goto out_err; 234662306a36Sopenharmony_ci } 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci recv_msg->user = user; 234962306a36Sopenharmony_ci if (user) 235062306a36Sopenharmony_ci /* The put happens when the message is freed. */ 235162306a36Sopenharmony_ci kref_get(&user->refcount); 235262306a36Sopenharmony_ci recv_msg->msgid = msgid; 235362306a36Sopenharmony_ci /* 235462306a36Sopenharmony_ci * Store the message to send in the receive message so timeout 235562306a36Sopenharmony_ci * responses can get the proper response data. 235662306a36Sopenharmony_ci */ 235762306a36Sopenharmony_ci recv_msg->msg = *msg; 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { 236062306a36Sopenharmony_ci rv = i_ipmi_req_sysintf(intf, addr, msgid, msg, smi_msg, 236162306a36Sopenharmony_ci recv_msg, retries, retry_time_ms); 236262306a36Sopenharmony_ci } else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { 236362306a36Sopenharmony_ci rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg, 236462306a36Sopenharmony_ci source_address, source_lun, 236562306a36Sopenharmony_ci retries, retry_time_ms); 236662306a36Sopenharmony_ci } else if (is_ipmb_direct_addr(addr)) { 236762306a36Sopenharmony_ci rv = i_ipmi_req_ipmb_direct(intf, addr, msgid, msg, smi_msg, 236862306a36Sopenharmony_ci recv_msg, source_lun); 236962306a36Sopenharmony_ci } else if (is_lan_addr(addr)) { 237062306a36Sopenharmony_ci rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg, 237162306a36Sopenharmony_ci source_lun, retries, retry_time_ms); 237262306a36Sopenharmony_ci } else { 237362306a36Sopenharmony_ci /* Unknown address type. */ 237462306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 237562306a36Sopenharmony_ci rv = -EINVAL; 237662306a36Sopenharmony_ci } 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci if (rv) { 237962306a36Sopenharmony_ciout_err: 238062306a36Sopenharmony_ci ipmi_free_smi_msg(smi_msg); 238162306a36Sopenharmony_ci ipmi_free_recv_msg(recv_msg); 238262306a36Sopenharmony_ci } else { 238362306a36Sopenharmony_ci dev_dbg(intf->si_dev, "Send: %*ph\n", 238462306a36Sopenharmony_ci smi_msg->data_size, smi_msg->data); 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci smi_send(intf, intf->handlers, smi_msg, priority); 238762306a36Sopenharmony_ci } 238862306a36Sopenharmony_ci rcu_read_unlock(); 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ciout: 239162306a36Sopenharmony_ci if (rv && user) 239262306a36Sopenharmony_ci atomic_dec(&user->nr_msgs); 239362306a36Sopenharmony_ci return rv; 239462306a36Sopenharmony_ci} 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_cistatic int check_addr(struct ipmi_smi *intf, 239762306a36Sopenharmony_ci struct ipmi_addr *addr, 239862306a36Sopenharmony_ci unsigned char *saddr, 239962306a36Sopenharmony_ci unsigned char *lun) 240062306a36Sopenharmony_ci{ 240162306a36Sopenharmony_ci if (addr->channel >= IPMI_MAX_CHANNELS) 240262306a36Sopenharmony_ci return -EINVAL; 240362306a36Sopenharmony_ci addr->channel = array_index_nospec(addr->channel, IPMI_MAX_CHANNELS); 240462306a36Sopenharmony_ci *lun = intf->addrinfo[addr->channel].lun; 240562306a36Sopenharmony_ci *saddr = intf->addrinfo[addr->channel].address; 240662306a36Sopenharmony_ci return 0; 240762306a36Sopenharmony_ci} 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ciint ipmi_request_settime(struct ipmi_user *user, 241062306a36Sopenharmony_ci struct ipmi_addr *addr, 241162306a36Sopenharmony_ci long msgid, 241262306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 241362306a36Sopenharmony_ci void *user_msg_data, 241462306a36Sopenharmony_ci int priority, 241562306a36Sopenharmony_ci int retries, 241662306a36Sopenharmony_ci unsigned int retry_time_ms) 241762306a36Sopenharmony_ci{ 241862306a36Sopenharmony_ci unsigned char saddr = 0, lun = 0; 241962306a36Sopenharmony_ci int rv, index; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci if (!user) 242262306a36Sopenharmony_ci return -EINVAL; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 242562306a36Sopenharmony_ci if (!user) 242662306a36Sopenharmony_ci return -ENODEV; 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci rv = check_addr(user->intf, addr, &saddr, &lun); 242962306a36Sopenharmony_ci if (!rv) 243062306a36Sopenharmony_ci rv = i_ipmi_request(user, 243162306a36Sopenharmony_ci user->intf, 243262306a36Sopenharmony_ci addr, 243362306a36Sopenharmony_ci msgid, 243462306a36Sopenharmony_ci msg, 243562306a36Sopenharmony_ci user_msg_data, 243662306a36Sopenharmony_ci NULL, NULL, 243762306a36Sopenharmony_ci priority, 243862306a36Sopenharmony_ci saddr, 243962306a36Sopenharmony_ci lun, 244062306a36Sopenharmony_ci retries, 244162306a36Sopenharmony_ci retry_time_ms); 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci release_ipmi_user(user, index); 244462306a36Sopenharmony_ci return rv; 244562306a36Sopenharmony_ci} 244662306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_request_settime); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ciint ipmi_request_supply_msgs(struct ipmi_user *user, 244962306a36Sopenharmony_ci struct ipmi_addr *addr, 245062306a36Sopenharmony_ci long msgid, 245162306a36Sopenharmony_ci struct kernel_ipmi_msg *msg, 245262306a36Sopenharmony_ci void *user_msg_data, 245362306a36Sopenharmony_ci void *supplied_smi, 245462306a36Sopenharmony_ci struct ipmi_recv_msg *supplied_recv, 245562306a36Sopenharmony_ci int priority) 245662306a36Sopenharmony_ci{ 245762306a36Sopenharmony_ci unsigned char saddr = 0, lun = 0; 245862306a36Sopenharmony_ci int rv, index; 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci if (!user) 246162306a36Sopenharmony_ci return -EINVAL; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci user = acquire_ipmi_user(user, &index); 246462306a36Sopenharmony_ci if (!user) 246562306a36Sopenharmony_ci return -ENODEV; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci rv = check_addr(user->intf, addr, &saddr, &lun); 246862306a36Sopenharmony_ci if (!rv) 246962306a36Sopenharmony_ci rv = i_ipmi_request(user, 247062306a36Sopenharmony_ci user->intf, 247162306a36Sopenharmony_ci addr, 247262306a36Sopenharmony_ci msgid, 247362306a36Sopenharmony_ci msg, 247462306a36Sopenharmony_ci user_msg_data, 247562306a36Sopenharmony_ci supplied_smi, 247662306a36Sopenharmony_ci supplied_recv, 247762306a36Sopenharmony_ci priority, 247862306a36Sopenharmony_ci saddr, 247962306a36Sopenharmony_ci lun, 248062306a36Sopenharmony_ci -1, 0); 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci release_ipmi_user(user, index); 248362306a36Sopenharmony_ci return rv; 248462306a36Sopenharmony_ci} 248562306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_request_supply_msgs); 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_cistatic void bmc_device_id_handler(struct ipmi_smi *intf, 248862306a36Sopenharmony_ci struct ipmi_recv_msg *msg) 248962306a36Sopenharmony_ci{ 249062306a36Sopenharmony_ci int rv; 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 249362306a36Sopenharmony_ci || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) 249462306a36Sopenharmony_ci || (msg->msg.cmd != IPMI_GET_DEVICE_ID_CMD)) { 249562306a36Sopenharmony_ci dev_warn(intf->si_dev, 249662306a36Sopenharmony_ci "invalid device_id msg: addr_type=%d netfn=%x cmd=%x\n", 249762306a36Sopenharmony_ci msg->addr.addr_type, msg->msg.netfn, msg->msg.cmd); 249862306a36Sopenharmony_ci return; 249962306a36Sopenharmony_ci } 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci if (msg->msg.data[0]) { 250262306a36Sopenharmony_ci dev_warn(intf->si_dev, "device id fetch failed: 0x%2.2x\n", 250362306a36Sopenharmony_ci msg->msg.data[0]); 250462306a36Sopenharmony_ci intf->bmc->dyn_id_set = 0; 250562306a36Sopenharmony_ci goto out; 250662306a36Sopenharmony_ci } 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd, 250962306a36Sopenharmony_ci msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id); 251062306a36Sopenharmony_ci if (rv) { 251162306a36Sopenharmony_ci dev_warn(intf->si_dev, "device id demangle failed: %d\n", rv); 251262306a36Sopenharmony_ci /* record completion code when error */ 251362306a36Sopenharmony_ci intf->bmc->cc = msg->msg.data[0]; 251462306a36Sopenharmony_ci intf->bmc->dyn_id_set = 0; 251562306a36Sopenharmony_ci } else { 251662306a36Sopenharmony_ci /* 251762306a36Sopenharmony_ci * Make sure the id data is available before setting 251862306a36Sopenharmony_ci * dyn_id_set. 251962306a36Sopenharmony_ci */ 252062306a36Sopenharmony_ci smp_wmb(); 252162306a36Sopenharmony_ci intf->bmc->dyn_id_set = 1; 252262306a36Sopenharmony_ci } 252362306a36Sopenharmony_ciout: 252462306a36Sopenharmony_ci wake_up(&intf->waitq); 252562306a36Sopenharmony_ci} 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_cistatic int 252862306a36Sopenharmony_cisend_get_device_id_cmd(struct ipmi_smi *intf) 252962306a36Sopenharmony_ci{ 253062306a36Sopenharmony_ci struct ipmi_system_interface_addr si; 253162306a36Sopenharmony_ci struct kernel_ipmi_msg msg; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 253462306a36Sopenharmony_ci si.channel = IPMI_BMC_CHANNEL; 253562306a36Sopenharmony_ci si.lun = 0; 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci msg.netfn = IPMI_NETFN_APP_REQUEST; 253862306a36Sopenharmony_ci msg.cmd = IPMI_GET_DEVICE_ID_CMD; 253962306a36Sopenharmony_ci msg.data = NULL; 254062306a36Sopenharmony_ci msg.data_len = 0; 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci return i_ipmi_request(NULL, 254362306a36Sopenharmony_ci intf, 254462306a36Sopenharmony_ci (struct ipmi_addr *) &si, 254562306a36Sopenharmony_ci 0, 254662306a36Sopenharmony_ci &msg, 254762306a36Sopenharmony_ci intf, 254862306a36Sopenharmony_ci NULL, 254962306a36Sopenharmony_ci NULL, 255062306a36Sopenharmony_ci 0, 255162306a36Sopenharmony_ci intf->addrinfo[0].address, 255262306a36Sopenharmony_ci intf->addrinfo[0].lun, 255362306a36Sopenharmony_ci -1, 0); 255462306a36Sopenharmony_ci} 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_cistatic int __get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc) 255762306a36Sopenharmony_ci{ 255862306a36Sopenharmony_ci int rv; 255962306a36Sopenharmony_ci unsigned int retry_count = 0; 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci intf->null_user_handler = bmc_device_id_handler; 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ciretry: 256462306a36Sopenharmony_ci bmc->cc = 0; 256562306a36Sopenharmony_ci bmc->dyn_id_set = 2; 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_ci rv = send_get_device_id_cmd(intf); 256862306a36Sopenharmony_ci if (rv) 256962306a36Sopenharmony_ci goto out_reset_handler; 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci wait_event(intf->waitq, bmc->dyn_id_set != 2); 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci if (!bmc->dyn_id_set) { 257462306a36Sopenharmony_ci if (bmc->cc != IPMI_CC_NO_ERROR && 257562306a36Sopenharmony_ci ++retry_count <= GET_DEVICE_ID_MAX_RETRY) { 257662306a36Sopenharmony_ci msleep(500); 257762306a36Sopenharmony_ci dev_warn(intf->si_dev, 257862306a36Sopenharmony_ci "BMC returned 0x%2.2x, retry get bmc device id\n", 257962306a36Sopenharmony_ci bmc->cc); 258062306a36Sopenharmony_ci goto retry; 258162306a36Sopenharmony_ci } 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci rv = -EIO; /* Something went wrong in the fetch. */ 258462306a36Sopenharmony_ci } 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci /* dyn_id_set makes the id data available. */ 258762306a36Sopenharmony_ci smp_rmb(); 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ciout_reset_handler: 259062306a36Sopenharmony_ci intf->null_user_handler = NULL; 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci return rv; 259362306a36Sopenharmony_ci} 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci/* 259662306a36Sopenharmony_ci * Fetch the device id for the bmc/interface. You must pass in either 259762306a36Sopenharmony_ci * bmc or intf, this code will get the other one. If the data has 259862306a36Sopenharmony_ci * been recently fetched, this will just use the cached data. Otherwise 259962306a36Sopenharmony_ci * it will run a new fetch. 260062306a36Sopenharmony_ci * 260162306a36Sopenharmony_ci * Except for the first time this is called (in ipmi_add_smi()), 260262306a36Sopenharmony_ci * this will always return good data; 260362306a36Sopenharmony_ci */ 260462306a36Sopenharmony_cistatic int __bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, 260562306a36Sopenharmony_ci struct ipmi_device_id *id, 260662306a36Sopenharmony_ci bool *guid_set, guid_t *guid, int intf_num) 260762306a36Sopenharmony_ci{ 260862306a36Sopenharmony_ci int rv = 0; 260962306a36Sopenharmony_ci int prev_dyn_id_set, prev_guid_set; 261062306a36Sopenharmony_ci bool intf_set = intf != NULL; 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci if (!intf) { 261362306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 261462306a36Sopenharmony_ciretry_bmc_lock: 261562306a36Sopenharmony_ci if (list_empty(&bmc->intfs)) { 261662306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 261762306a36Sopenharmony_ci return -ENOENT; 261862306a36Sopenharmony_ci } 261962306a36Sopenharmony_ci intf = list_first_entry(&bmc->intfs, struct ipmi_smi, 262062306a36Sopenharmony_ci bmc_link); 262162306a36Sopenharmony_ci kref_get(&intf->refcount); 262262306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 262362306a36Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 262462306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 262562306a36Sopenharmony_ci if (intf != list_first_entry(&bmc->intfs, struct ipmi_smi, 262662306a36Sopenharmony_ci bmc_link)) { 262762306a36Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 262862306a36Sopenharmony_ci kref_put(&intf->refcount, intf_free); 262962306a36Sopenharmony_ci goto retry_bmc_lock; 263062306a36Sopenharmony_ci } 263162306a36Sopenharmony_ci } else { 263262306a36Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 263362306a36Sopenharmony_ci bmc = intf->bmc; 263462306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 263562306a36Sopenharmony_ci kref_get(&intf->refcount); 263662306a36Sopenharmony_ci } 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci /* If we have a valid and current ID, just return that. */ 263962306a36Sopenharmony_ci if (intf->in_bmc_register || 264062306a36Sopenharmony_ci (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry))) 264162306a36Sopenharmony_ci goto out_noprocessing; 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci prev_guid_set = bmc->dyn_guid_set; 264462306a36Sopenharmony_ci __get_guid(intf); 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci prev_dyn_id_set = bmc->dyn_id_set; 264762306a36Sopenharmony_ci rv = __get_device_id(intf, bmc); 264862306a36Sopenharmony_ci if (rv) 264962306a36Sopenharmony_ci goto out; 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci /* 265262306a36Sopenharmony_ci * The guid, device id, manufacturer id, and product id should 265362306a36Sopenharmony_ci * not change on a BMC. If it does we have to do some dancing. 265462306a36Sopenharmony_ci */ 265562306a36Sopenharmony_ci if (!intf->bmc_registered 265662306a36Sopenharmony_ci || (!prev_guid_set && bmc->dyn_guid_set) 265762306a36Sopenharmony_ci || (!prev_dyn_id_set && bmc->dyn_id_set) 265862306a36Sopenharmony_ci || (prev_guid_set && bmc->dyn_guid_set 265962306a36Sopenharmony_ci && !guid_equal(&bmc->guid, &bmc->fetch_guid)) 266062306a36Sopenharmony_ci || bmc->id.device_id != bmc->fetch_id.device_id 266162306a36Sopenharmony_ci || bmc->id.manufacturer_id != bmc->fetch_id.manufacturer_id 266262306a36Sopenharmony_ci || bmc->id.product_id != bmc->fetch_id.product_id) { 266362306a36Sopenharmony_ci struct ipmi_device_id id = bmc->fetch_id; 266462306a36Sopenharmony_ci int guid_set = bmc->dyn_guid_set; 266562306a36Sopenharmony_ci guid_t guid; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci guid = bmc->fetch_guid; 266862306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci __ipmi_bmc_unregister(intf); 267162306a36Sopenharmony_ci /* Fill in the temporary BMC for good measure. */ 267262306a36Sopenharmony_ci intf->bmc->id = id; 267362306a36Sopenharmony_ci intf->bmc->dyn_guid_set = guid_set; 267462306a36Sopenharmony_ci intf->bmc->guid = guid; 267562306a36Sopenharmony_ci if (__ipmi_bmc_register(intf, &id, guid_set, &guid, intf_num)) 267662306a36Sopenharmony_ci need_waiter(intf); /* Retry later on an error. */ 267762306a36Sopenharmony_ci else 267862306a36Sopenharmony_ci __scan_channels(intf, &id); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci if (!intf_set) { 268262306a36Sopenharmony_ci /* 268362306a36Sopenharmony_ci * We weren't given the interface on the 268462306a36Sopenharmony_ci * command line, so restart the operation on 268562306a36Sopenharmony_ci * the next interface for the BMC. 268662306a36Sopenharmony_ci */ 268762306a36Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 268862306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 268962306a36Sopenharmony_ci goto retry_bmc_lock; 269062306a36Sopenharmony_ci } 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci /* We have a new BMC, set it up. */ 269362306a36Sopenharmony_ci bmc = intf->bmc; 269462306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 269562306a36Sopenharmony_ci goto out_noprocessing; 269662306a36Sopenharmony_ci } else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id))) 269762306a36Sopenharmony_ci /* Version info changes, scan the channels again. */ 269862306a36Sopenharmony_ci __scan_channels(intf, &bmc->fetch_id); 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY; 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ciout: 270362306a36Sopenharmony_ci if (rv && prev_dyn_id_set) { 270462306a36Sopenharmony_ci rv = 0; /* Ignore failures if we have previous data. */ 270562306a36Sopenharmony_ci bmc->dyn_id_set = prev_dyn_id_set; 270662306a36Sopenharmony_ci } 270762306a36Sopenharmony_ci if (!rv) { 270862306a36Sopenharmony_ci bmc->id = bmc->fetch_id; 270962306a36Sopenharmony_ci if (bmc->dyn_guid_set) 271062306a36Sopenharmony_ci bmc->guid = bmc->fetch_guid; 271162306a36Sopenharmony_ci else if (prev_guid_set) 271262306a36Sopenharmony_ci /* 271362306a36Sopenharmony_ci * The guid used to be valid and it failed to fetch, 271462306a36Sopenharmony_ci * just use the cached value. 271562306a36Sopenharmony_ci */ 271662306a36Sopenharmony_ci bmc->dyn_guid_set = prev_guid_set; 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ciout_noprocessing: 271962306a36Sopenharmony_ci if (!rv) { 272062306a36Sopenharmony_ci if (id) 272162306a36Sopenharmony_ci *id = bmc->id; 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci if (guid_set) 272462306a36Sopenharmony_ci *guid_set = bmc->dyn_guid_set; 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci if (guid && bmc->dyn_guid_set) 272762306a36Sopenharmony_ci *guid = bmc->guid; 272862306a36Sopenharmony_ci } 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 273162306a36Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci kref_put(&intf->refcount, intf_free); 273462306a36Sopenharmony_ci return rv; 273562306a36Sopenharmony_ci} 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_cistatic int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, 273862306a36Sopenharmony_ci struct ipmi_device_id *id, 273962306a36Sopenharmony_ci bool *guid_set, guid_t *guid) 274062306a36Sopenharmony_ci{ 274162306a36Sopenharmony_ci return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1); 274262306a36Sopenharmony_ci} 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_cistatic ssize_t device_id_show(struct device *dev, 274562306a36Sopenharmony_ci struct device_attribute *attr, 274662306a36Sopenharmony_ci char *buf) 274762306a36Sopenharmony_ci{ 274862306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 274962306a36Sopenharmony_ci struct ipmi_device_id id; 275062306a36Sopenharmony_ci int rv; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 275362306a36Sopenharmony_ci if (rv) 275462306a36Sopenharmony_ci return rv; 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", id.device_id); 275762306a36Sopenharmony_ci} 275862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(device_id); 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_cistatic ssize_t provides_device_sdrs_show(struct device *dev, 276162306a36Sopenharmony_ci struct device_attribute *attr, 276262306a36Sopenharmony_ci char *buf) 276362306a36Sopenharmony_ci{ 276462306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 276562306a36Sopenharmony_ci struct ipmi_device_id id; 276662306a36Sopenharmony_ci int rv; 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 276962306a36Sopenharmony_ci if (rv) 277062306a36Sopenharmony_ci return rv; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", (id.device_revision & 0x80) >> 7); 277362306a36Sopenharmony_ci} 277462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(provides_device_sdrs); 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_cistatic ssize_t revision_show(struct device *dev, struct device_attribute *attr, 277762306a36Sopenharmony_ci char *buf) 277862306a36Sopenharmony_ci{ 277962306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 278062306a36Sopenharmony_ci struct ipmi_device_id id; 278162306a36Sopenharmony_ci int rv; 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 278462306a36Sopenharmony_ci if (rv) 278562306a36Sopenharmony_ci return rv; 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", id.device_revision & 0x0F); 278862306a36Sopenharmony_ci} 278962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(revision); 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_cistatic ssize_t firmware_revision_show(struct device *dev, 279262306a36Sopenharmony_ci struct device_attribute *attr, 279362306a36Sopenharmony_ci char *buf) 279462306a36Sopenharmony_ci{ 279562306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 279662306a36Sopenharmony_ci struct ipmi_device_id id; 279762306a36Sopenharmony_ci int rv; 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 280062306a36Sopenharmony_ci if (rv) 280162306a36Sopenharmony_ci return rv; 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci return sysfs_emit(buf, "%u.%x\n", id.firmware_revision_1, 280462306a36Sopenharmony_ci id.firmware_revision_2); 280562306a36Sopenharmony_ci} 280662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_revision); 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_cistatic ssize_t ipmi_version_show(struct device *dev, 280962306a36Sopenharmony_ci struct device_attribute *attr, 281062306a36Sopenharmony_ci char *buf) 281162306a36Sopenharmony_ci{ 281262306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 281362306a36Sopenharmony_ci struct ipmi_device_id id; 281462306a36Sopenharmony_ci int rv; 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 281762306a36Sopenharmony_ci if (rv) 281862306a36Sopenharmony_ci return rv; 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci return sysfs_emit(buf, "%u.%u\n", 282162306a36Sopenharmony_ci ipmi_version_major(&id), 282262306a36Sopenharmony_ci ipmi_version_minor(&id)); 282362306a36Sopenharmony_ci} 282462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ipmi_version); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_cistatic ssize_t add_dev_support_show(struct device *dev, 282762306a36Sopenharmony_ci struct device_attribute *attr, 282862306a36Sopenharmony_ci char *buf) 282962306a36Sopenharmony_ci{ 283062306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 283162306a36Sopenharmony_ci struct ipmi_device_id id; 283262306a36Sopenharmony_ci int rv; 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 283562306a36Sopenharmony_ci if (rv) 283662306a36Sopenharmony_ci return rv; 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci return sysfs_emit(buf, "0x%02x\n", id.additional_device_support); 283962306a36Sopenharmony_ci} 284062306a36Sopenharmony_cistatic DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show, 284162306a36Sopenharmony_ci NULL); 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_cistatic ssize_t manufacturer_id_show(struct device *dev, 284462306a36Sopenharmony_ci struct device_attribute *attr, 284562306a36Sopenharmony_ci char *buf) 284662306a36Sopenharmony_ci{ 284762306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 284862306a36Sopenharmony_ci struct ipmi_device_id id; 284962306a36Sopenharmony_ci int rv; 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 285262306a36Sopenharmony_ci if (rv) 285362306a36Sopenharmony_ci return rv; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci return sysfs_emit(buf, "0x%6.6x\n", id.manufacturer_id); 285662306a36Sopenharmony_ci} 285762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(manufacturer_id); 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_cistatic ssize_t product_id_show(struct device *dev, 286062306a36Sopenharmony_ci struct device_attribute *attr, 286162306a36Sopenharmony_ci char *buf) 286262306a36Sopenharmony_ci{ 286362306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 286462306a36Sopenharmony_ci struct ipmi_device_id id; 286562306a36Sopenharmony_ci int rv; 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 286862306a36Sopenharmony_ci if (rv) 286962306a36Sopenharmony_ci return rv; 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci return sysfs_emit(buf, "0x%4.4x\n", id.product_id); 287262306a36Sopenharmony_ci} 287362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(product_id); 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_cistatic ssize_t aux_firmware_rev_show(struct device *dev, 287662306a36Sopenharmony_ci struct device_attribute *attr, 287762306a36Sopenharmony_ci char *buf) 287862306a36Sopenharmony_ci{ 287962306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 288062306a36Sopenharmony_ci struct ipmi_device_id id; 288162306a36Sopenharmony_ci int rv; 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 288462306a36Sopenharmony_ci if (rv) 288562306a36Sopenharmony_ci return rv; 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci return sysfs_emit(buf, "0x%02x 0x%02x 0x%02x 0x%02x\n", 288862306a36Sopenharmony_ci id.aux_firmware_revision[3], 288962306a36Sopenharmony_ci id.aux_firmware_revision[2], 289062306a36Sopenharmony_ci id.aux_firmware_revision[1], 289162306a36Sopenharmony_ci id.aux_firmware_revision[0]); 289262306a36Sopenharmony_ci} 289362306a36Sopenharmony_cistatic DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL); 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_cistatic ssize_t guid_show(struct device *dev, struct device_attribute *attr, 289662306a36Sopenharmony_ci char *buf) 289762306a36Sopenharmony_ci{ 289862306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 289962306a36Sopenharmony_ci bool guid_set; 290062306a36Sopenharmony_ci guid_t guid; 290162306a36Sopenharmony_ci int rv; 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, &guid); 290462306a36Sopenharmony_ci if (rv) 290562306a36Sopenharmony_ci return rv; 290662306a36Sopenharmony_ci if (!guid_set) 290762306a36Sopenharmony_ci return -ENOENT; 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci return sysfs_emit(buf, "%pUl\n", &guid); 291062306a36Sopenharmony_ci} 291162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(guid); 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_cistatic struct attribute *bmc_dev_attrs[] = { 291462306a36Sopenharmony_ci &dev_attr_device_id.attr, 291562306a36Sopenharmony_ci &dev_attr_provides_device_sdrs.attr, 291662306a36Sopenharmony_ci &dev_attr_revision.attr, 291762306a36Sopenharmony_ci &dev_attr_firmware_revision.attr, 291862306a36Sopenharmony_ci &dev_attr_ipmi_version.attr, 291962306a36Sopenharmony_ci &dev_attr_additional_device_support.attr, 292062306a36Sopenharmony_ci &dev_attr_manufacturer_id.attr, 292162306a36Sopenharmony_ci &dev_attr_product_id.attr, 292262306a36Sopenharmony_ci &dev_attr_aux_firmware_revision.attr, 292362306a36Sopenharmony_ci &dev_attr_guid.attr, 292462306a36Sopenharmony_ci NULL 292562306a36Sopenharmony_ci}; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_cistatic umode_t bmc_dev_attr_is_visible(struct kobject *kobj, 292862306a36Sopenharmony_ci struct attribute *attr, int idx) 292962306a36Sopenharmony_ci{ 293062306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 293162306a36Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 293262306a36Sopenharmony_ci umode_t mode = attr->mode; 293362306a36Sopenharmony_ci int rv; 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci if (attr == &dev_attr_aux_firmware_revision.attr) { 293662306a36Sopenharmony_ci struct ipmi_device_id id; 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 293962306a36Sopenharmony_ci return (!rv && id.aux_firmware_revision_set) ? mode : 0; 294062306a36Sopenharmony_ci } 294162306a36Sopenharmony_ci if (attr == &dev_attr_guid.attr) { 294262306a36Sopenharmony_ci bool guid_set; 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, NULL); 294562306a36Sopenharmony_ci return (!rv && guid_set) ? mode : 0; 294662306a36Sopenharmony_ci } 294762306a36Sopenharmony_ci return mode; 294862306a36Sopenharmony_ci} 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_cistatic const struct attribute_group bmc_dev_attr_group = { 295162306a36Sopenharmony_ci .attrs = bmc_dev_attrs, 295262306a36Sopenharmony_ci .is_visible = bmc_dev_attr_is_visible, 295362306a36Sopenharmony_ci}; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_cistatic const struct attribute_group *bmc_dev_attr_groups[] = { 295662306a36Sopenharmony_ci &bmc_dev_attr_group, 295762306a36Sopenharmony_ci NULL 295862306a36Sopenharmony_ci}; 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_cistatic const struct device_type bmc_device_type = { 296162306a36Sopenharmony_ci .groups = bmc_dev_attr_groups, 296262306a36Sopenharmony_ci}; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_cistatic int __find_bmc_guid(struct device *dev, const void *data) 296562306a36Sopenharmony_ci{ 296662306a36Sopenharmony_ci const guid_t *guid = data; 296762306a36Sopenharmony_ci struct bmc_device *bmc; 296862306a36Sopenharmony_ci int rv; 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ci if (dev->type != &bmc_device_type) 297162306a36Sopenharmony_ci return 0; 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci bmc = to_bmc_device(dev); 297462306a36Sopenharmony_ci rv = bmc->dyn_guid_set && guid_equal(&bmc->guid, guid); 297562306a36Sopenharmony_ci if (rv) 297662306a36Sopenharmony_ci rv = kref_get_unless_zero(&bmc->usecount); 297762306a36Sopenharmony_ci return rv; 297862306a36Sopenharmony_ci} 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci/* 298162306a36Sopenharmony_ci * Returns with the bmc's usecount incremented, if it is non-NULL. 298262306a36Sopenharmony_ci */ 298362306a36Sopenharmony_cistatic struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv, 298462306a36Sopenharmony_ci guid_t *guid) 298562306a36Sopenharmony_ci{ 298662306a36Sopenharmony_ci struct device *dev; 298762306a36Sopenharmony_ci struct bmc_device *bmc = NULL; 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci dev = driver_find_device(drv, NULL, guid, __find_bmc_guid); 299062306a36Sopenharmony_ci if (dev) { 299162306a36Sopenharmony_ci bmc = to_bmc_device(dev); 299262306a36Sopenharmony_ci put_device(dev); 299362306a36Sopenharmony_ci } 299462306a36Sopenharmony_ci return bmc; 299562306a36Sopenharmony_ci} 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_cistruct prod_dev_id { 299862306a36Sopenharmony_ci unsigned int product_id; 299962306a36Sopenharmony_ci unsigned char device_id; 300062306a36Sopenharmony_ci}; 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_cistatic int __find_bmc_prod_dev_id(struct device *dev, const void *data) 300362306a36Sopenharmony_ci{ 300462306a36Sopenharmony_ci const struct prod_dev_id *cid = data; 300562306a36Sopenharmony_ci struct bmc_device *bmc; 300662306a36Sopenharmony_ci int rv; 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci if (dev->type != &bmc_device_type) 300962306a36Sopenharmony_ci return 0; 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci bmc = to_bmc_device(dev); 301262306a36Sopenharmony_ci rv = (bmc->id.product_id == cid->product_id 301362306a36Sopenharmony_ci && bmc->id.device_id == cid->device_id); 301462306a36Sopenharmony_ci if (rv) 301562306a36Sopenharmony_ci rv = kref_get_unless_zero(&bmc->usecount); 301662306a36Sopenharmony_ci return rv; 301762306a36Sopenharmony_ci} 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci/* 302062306a36Sopenharmony_ci * Returns with the bmc's usecount incremented, if it is non-NULL. 302162306a36Sopenharmony_ci */ 302262306a36Sopenharmony_cistatic struct bmc_device *ipmi_find_bmc_prod_dev_id( 302362306a36Sopenharmony_ci struct device_driver *drv, 302462306a36Sopenharmony_ci unsigned int product_id, unsigned char device_id) 302562306a36Sopenharmony_ci{ 302662306a36Sopenharmony_ci struct prod_dev_id id = { 302762306a36Sopenharmony_ci .product_id = product_id, 302862306a36Sopenharmony_ci .device_id = device_id, 302962306a36Sopenharmony_ci }; 303062306a36Sopenharmony_ci struct device *dev; 303162306a36Sopenharmony_ci struct bmc_device *bmc = NULL; 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id); 303462306a36Sopenharmony_ci if (dev) { 303562306a36Sopenharmony_ci bmc = to_bmc_device(dev); 303662306a36Sopenharmony_ci put_device(dev); 303762306a36Sopenharmony_ci } 303862306a36Sopenharmony_ci return bmc; 303962306a36Sopenharmony_ci} 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_cistatic DEFINE_IDA(ipmi_bmc_ida); 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_cistatic void 304462306a36Sopenharmony_cirelease_bmc_device(struct device *dev) 304562306a36Sopenharmony_ci{ 304662306a36Sopenharmony_ci kfree(to_bmc_device(dev)); 304762306a36Sopenharmony_ci} 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_cistatic void cleanup_bmc_work(struct work_struct *work) 305062306a36Sopenharmony_ci{ 305162306a36Sopenharmony_ci struct bmc_device *bmc = container_of(work, struct bmc_device, 305262306a36Sopenharmony_ci remove_work); 305362306a36Sopenharmony_ci int id = bmc->pdev.id; /* Unregister overwrites id */ 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci platform_device_unregister(&bmc->pdev); 305662306a36Sopenharmony_ci ida_simple_remove(&ipmi_bmc_ida, id); 305762306a36Sopenharmony_ci} 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_cistatic void 306062306a36Sopenharmony_cicleanup_bmc_device(struct kref *ref) 306162306a36Sopenharmony_ci{ 306262306a36Sopenharmony_ci struct bmc_device *bmc = container_of(ref, struct bmc_device, usecount); 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci /* 306562306a36Sopenharmony_ci * Remove the platform device in a work queue to avoid issues 306662306a36Sopenharmony_ci * with removing the device attributes while reading a device 306762306a36Sopenharmony_ci * attribute. 306862306a36Sopenharmony_ci */ 306962306a36Sopenharmony_ci queue_work(remove_work_wq, &bmc->remove_work); 307062306a36Sopenharmony_ci} 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci/* 307362306a36Sopenharmony_ci * Must be called with intf->bmc_reg_mutex held. 307462306a36Sopenharmony_ci */ 307562306a36Sopenharmony_cistatic void __ipmi_bmc_unregister(struct ipmi_smi *intf) 307662306a36Sopenharmony_ci{ 307762306a36Sopenharmony_ci struct bmc_device *bmc = intf->bmc; 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_ci if (!intf->bmc_registered) 308062306a36Sopenharmony_ci return; 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci sysfs_remove_link(&intf->si_dev->kobj, "bmc"); 308362306a36Sopenharmony_ci sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name); 308462306a36Sopenharmony_ci kfree(intf->my_dev_name); 308562306a36Sopenharmony_ci intf->my_dev_name = NULL; 308662306a36Sopenharmony_ci 308762306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 308862306a36Sopenharmony_ci list_del(&intf->bmc_link); 308962306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 309062306a36Sopenharmony_ci intf->bmc = &intf->tmp_bmc; 309162306a36Sopenharmony_ci kref_put(&bmc->usecount, cleanup_bmc_device); 309262306a36Sopenharmony_ci intf->bmc_registered = false; 309362306a36Sopenharmony_ci} 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_cistatic void ipmi_bmc_unregister(struct ipmi_smi *intf) 309662306a36Sopenharmony_ci{ 309762306a36Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 309862306a36Sopenharmony_ci __ipmi_bmc_unregister(intf); 309962306a36Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 310062306a36Sopenharmony_ci} 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci/* 310362306a36Sopenharmony_ci * Must be called with intf->bmc_reg_mutex held. 310462306a36Sopenharmony_ci */ 310562306a36Sopenharmony_cistatic int __ipmi_bmc_register(struct ipmi_smi *intf, 310662306a36Sopenharmony_ci struct ipmi_device_id *id, 310762306a36Sopenharmony_ci bool guid_set, guid_t *guid, int intf_num) 310862306a36Sopenharmony_ci{ 310962306a36Sopenharmony_ci int rv; 311062306a36Sopenharmony_ci struct bmc_device *bmc; 311162306a36Sopenharmony_ci struct bmc_device *old_bmc; 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci /* 311462306a36Sopenharmony_ci * platform_device_register() can cause bmc_reg_mutex to 311562306a36Sopenharmony_ci * be claimed because of the is_visible functions of 311662306a36Sopenharmony_ci * the attributes. Eliminate possible recursion and 311762306a36Sopenharmony_ci * release the lock. 311862306a36Sopenharmony_ci */ 311962306a36Sopenharmony_ci intf->in_bmc_register = true; 312062306a36Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci /* 312362306a36Sopenharmony_ci * Try to find if there is an bmc_device struct 312462306a36Sopenharmony_ci * representing the interfaced BMC already 312562306a36Sopenharmony_ci */ 312662306a36Sopenharmony_ci mutex_lock(&ipmidriver_mutex); 312762306a36Sopenharmony_ci if (guid_set) 312862306a36Sopenharmony_ci old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, guid); 312962306a36Sopenharmony_ci else 313062306a36Sopenharmony_ci old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, 313162306a36Sopenharmony_ci id->product_id, 313262306a36Sopenharmony_ci id->device_id); 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci /* 313562306a36Sopenharmony_ci * If there is already an bmc_device, free the new one, 313662306a36Sopenharmony_ci * otherwise register the new BMC device 313762306a36Sopenharmony_ci */ 313862306a36Sopenharmony_ci if (old_bmc) { 313962306a36Sopenharmony_ci bmc = old_bmc; 314062306a36Sopenharmony_ci /* 314162306a36Sopenharmony_ci * Note: old_bmc already has usecount incremented by 314262306a36Sopenharmony_ci * the BMC find functions. 314362306a36Sopenharmony_ci */ 314462306a36Sopenharmony_ci intf->bmc = old_bmc; 314562306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 314662306a36Sopenharmony_ci list_add_tail(&intf->bmc_link, &bmc->intfs); 314762306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci dev_info(intf->si_dev, 315062306a36Sopenharmony_ci "interfacing existing BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", 315162306a36Sopenharmony_ci bmc->id.manufacturer_id, 315262306a36Sopenharmony_ci bmc->id.product_id, 315362306a36Sopenharmony_ci bmc->id.device_id); 315462306a36Sopenharmony_ci } else { 315562306a36Sopenharmony_ci bmc = kzalloc(sizeof(*bmc), GFP_KERNEL); 315662306a36Sopenharmony_ci if (!bmc) { 315762306a36Sopenharmony_ci rv = -ENOMEM; 315862306a36Sopenharmony_ci goto out; 315962306a36Sopenharmony_ci } 316062306a36Sopenharmony_ci INIT_LIST_HEAD(&bmc->intfs); 316162306a36Sopenharmony_ci mutex_init(&bmc->dyn_mutex); 316262306a36Sopenharmony_ci INIT_WORK(&bmc->remove_work, cleanup_bmc_work); 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_ci bmc->id = *id; 316562306a36Sopenharmony_ci bmc->dyn_id_set = 1; 316662306a36Sopenharmony_ci bmc->dyn_guid_set = guid_set; 316762306a36Sopenharmony_ci bmc->guid = *guid; 316862306a36Sopenharmony_ci bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY; 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci bmc->pdev.name = "ipmi_bmc"; 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL); 317362306a36Sopenharmony_ci if (rv < 0) { 317462306a36Sopenharmony_ci kfree(bmc); 317562306a36Sopenharmony_ci goto out; 317662306a36Sopenharmony_ci } 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci bmc->pdev.dev.driver = &ipmidriver.driver; 317962306a36Sopenharmony_ci bmc->pdev.id = rv; 318062306a36Sopenharmony_ci bmc->pdev.dev.release = release_bmc_device; 318162306a36Sopenharmony_ci bmc->pdev.dev.type = &bmc_device_type; 318262306a36Sopenharmony_ci kref_init(&bmc->usecount); 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci intf->bmc = bmc; 318562306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 318662306a36Sopenharmony_ci list_add_tail(&intf->bmc_link, &bmc->intfs); 318762306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci rv = platform_device_register(&bmc->pdev); 319062306a36Sopenharmony_ci if (rv) { 319162306a36Sopenharmony_ci dev_err(intf->si_dev, 319262306a36Sopenharmony_ci "Unable to register bmc device: %d\n", 319362306a36Sopenharmony_ci rv); 319462306a36Sopenharmony_ci goto out_list_del; 319562306a36Sopenharmony_ci } 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci dev_info(intf->si_dev, 319862306a36Sopenharmony_ci "Found new BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", 319962306a36Sopenharmony_ci bmc->id.manufacturer_id, 320062306a36Sopenharmony_ci bmc->id.product_id, 320162306a36Sopenharmony_ci bmc->id.device_id); 320262306a36Sopenharmony_ci } 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci /* 320562306a36Sopenharmony_ci * create symlink from system interface device to bmc device 320662306a36Sopenharmony_ci * and back. 320762306a36Sopenharmony_ci */ 320862306a36Sopenharmony_ci rv = sysfs_create_link(&intf->si_dev->kobj, &bmc->pdev.dev.kobj, "bmc"); 320962306a36Sopenharmony_ci if (rv) { 321062306a36Sopenharmony_ci dev_err(intf->si_dev, "Unable to create bmc symlink: %d\n", rv); 321162306a36Sopenharmony_ci goto out_put_bmc; 321262306a36Sopenharmony_ci } 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_ci if (intf_num == -1) 321562306a36Sopenharmony_ci intf_num = intf->intf_num; 321662306a36Sopenharmony_ci intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", intf_num); 321762306a36Sopenharmony_ci if (!intf->my_dev_name) { 321862306a36Sopenharmony_ci rv = -ENOMEM; 321962306a36Sopenharmony_ci dev_err(intf->si_dev, "Unable to allocate link from BMC: %d\n", 322062306a36Sopenharmony_ci rv); 322162306a36Sopenharmony_ci goto out_unlink1; 322262306a36Sopenharmony_ci } 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci rv = sysfs_create_link(&bmc->pdev.dev.kobj, &intf->si_dev->kobj, 322562306a36Sopenharmony_ci intf->my_dev_name); 322662306a36Sopenharmony_ci if (rv) { 322762306a36Sopenharmony_ci dev_err(intf->si_dev, "Unable to create symlink to bmc: %d\n", 322862306a36Sopenharmony_ci rv); 322962306a36Sopenharmony_ci goto out_free_my_dev_name; 323062306a36Sopenharmony_ci } 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci intf->bmc_registered = true; 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ciout: 323562306a36Sopenharmony_ci mutex_unlock(&ipmidriver_mutex); 323662306a36Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 323762306a36Sopenharmony_ci intf->in_bmc_register = false; 323862306a36Sopenharmony_ci return rv; 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ciout_free_my_dev_name: 324262306a36Sopenharmony_ci kfree(intf->my_dev_name); 324362306a36Sopenharmony_ci intf->my_dev_name = NULL; 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_ciout_unlink1: 324662306a36Sopenharmony_ci sysfs_remove_link(&intf->si_dev->kobj, "bmc"); 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ciout_put_bmc: 324962306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 325062306a36Sopenharmony_ci list_del(&intf->bmc_link); 325162306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 325262306a36Sopenharmony_ci intf->bmc = &intf->tmp_bmc; 325362306a36Sopenharmony_ci kref_put(&bmc->usecount, cleanup_bmc_device); 325462306a36Sopenharmony_ci goto out; 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ciout_list_del: 325762306a36Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 325862306a36Sopenharmony_ci list_del(&intf->bmc_link); 325962306a36Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 326062306a36Sopenharmony_ci intf->bmc = &intf->tmp_bmc; 326162306a36Sopenharmony_ci put_device(&bmc->pdev.dev); 326262306a36Sopenharmony_ci goto out; 326362306a36Sopenharmony_ci} 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_cistatic int 326662306a36Sopenharmony_cisend_guid_cmd(struct ipmi_smi *intf, int chan) 326762306a36Sopenharmony_ci{ 326862306a36Sopenharmony_ci struct kernel_ipmi_msg msg; 326962306a36Sopenharmony_ci struct ipmi_system_interface_addr si; 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 327262306a36Sopenharmony_ci si.channel = IPMI_BMC_CHANNEL; 327362306a36Sopenharmony_ci si.lun = 0; 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci msg.netfn = IPMI_NETFN_APP_REQUEST; 327662306a36Sopenharmony_ci msg.cmd = IPMI_GET_DEVICE_GUID_CMD; 327762306a36Sopenharmony_ci msg.data = NULL; 327862306a36Sopenharmony_ci msg.data_len = 0; 327962306a36Sopenharmony_ci return i_ipmi_request(NULL, 328062306a36Sopenharmony_ci intf, 328162306a36Sopenharmony_ci (struct ipmi_addr *) &si, 328262306a36Sopenharmony_ci 0, 328362306a36Sopenharmony_ci &msg, 328462306a36Sopenharmony_ci intf, 328562306a36Sopenharmony_ci NULL, 328662306a36Sopenharmony_ci NULL, 328762306a36Sopenharmony_ci 0, 328862306a36Sopenharmony_ci intf->addrinfo[0].address, 328962306a36Sopenharmony_ci intf->addrinfo[0].lun, 329062306a36Sopenharmony_ci -1, 0); 329162306a36Sopenharmony_ci} 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_cistatic void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) 329462306a36Sopenharmony_ci{ 329562306a36Sopenharmony_ci struct bmc_device *bmc = intf->bmc; 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 329862306a36Sopenharmony_ci || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) 329962306a36Sopenharmony_ci || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD)) 330062306a36Sopenharmony_ci /* Not for me */ 330162306a36Sopenharmony_ci return; 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci if (msg->msg.data[0] != 0) { 330462306a36Sopenharmony_ci /* Error from getting the GUID, the BMC doesn't have one. */ 330562306a36Sopenharmony_ci bmc->dyn_guid_set = 0; 330662306a36Sopenharmony_ci goto out; 330762306a36Sopenharmony_ci } 330862306a36Sopenharmony_ci 330962306a36Sopenharmony_ci if (msg->msg.data_len < UUID_SIZE + 1) { 331062306a36Sopenharmony_ci bmc->dyn_guid_set = 0; 331162306a36Sopenharmony_ci dev_warn(intf->si_dev, 331262306a36Sopenharmony_ci "The GUID response from the BMC was too short, it was %d but should have been %d. Assuming GUID is not available.\n", 331362306a36Sopenharmony_ci msg->msg.data_len, UUID_SIZE + 1); 331462306a36Sopenharmony_ci goto out; 331562306a36Sopenharmony_ci } 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci import_guid(&bmc->fetch_guid, msg->msg.data + 1); 331862306a36Sopenharmony_ci /* 331962306a36Sopenharmony_ci * Make sure the guid data is available before setting 332062306a36Sopenharmony_ci * dyn_guid_set. 332162306a36Sopenharmony_ci */ 332262306a36Sopenharmony_ci smp_wmb(); 332362306a36Sopenharmony_ci bmc->dyn_guid_set = 1; 332462306a36Sopenharmony_ci out: 332562306a36Sopenharmony_ci wake_up(&intf->waitq); 332662306a36Sopenharmony_ci} 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_cistatic void __get_guid(struct ipmi_smi *intf) 332962306a36Sopenharmony_ci{ 333062306a36Sopenharmony_ci int rv; 333162306a36Sopenharmony_ci struct bmc_device *bmc = intf->bmc; 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci bmc->dyn_guid_set = 2; 333462306a36Sopenharmony_ci intf->null_user_handler = guid_handler; 333562306a36Sopenharmony_ci rv = send_guid_cmd(intf, 0); 333662306a36Sopenharmony_ci if (rv) 333762306a36Sopenharmony_ci /* Send failed, no GUID available. */ 333862306a36Sopenharmony_ci bmc->dyn_guid_set = 0; 333962306a36Sopenharmony_ci else 334062306a36Sopenharmony_ci wait_event(intf->waitq, bmc->dyn_guid_set != 2); 334162306a36Sopenharmony_ci 334262306a36Sopenharmony_ci /* dyn_guid_set makes the guid data available. */ 334362306a36Sopenharmony_ci smp_rmb(); 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci intf->null_user_handler = NULL; 334662306a36Sopenharmony_ci} 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_cistatic int 334962306a36Sopenharmony_cisend_channel_info_cmd(struct ipmi_smi *intf, int chan) 335062306a36Sopenharmony_ci{ 335162306a36Sopenharmony_ci struct kernel_ipmi_msg msg; 335262306a36Sopenharmony_ci unsigned char data[1]; 335362306a36Sopenharmony_ci struct ipmi_system_interface_addr si; 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 335662306a36Sopenharmony_ci si.channel = IPMI_BMC_CHANNEL; 335762306a36Sopenharmony_ci si.lun = 0; 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci msg.netfn = IPMI_NETFN_APP_REQUEST; 336062306a36Sopenharmony_ci msg.cmd = IPMI_GET_CHANNEL_INFO_CMD; 336162306a36Sopenharmony_ci msg.data = data; 336262306a36Sopenharmony_ci msg.data_len = 1; 336362306a36Sopenharmony_ci data[0] = chan; 336462306a36Sopenharmony_ci return i_ipmi_request(NULL, 336562306a36Sopenharmony_ci intf, 336662306a36Sopenharmony_ci (struct ipmi_addr *) &si, 336762306a36Sopenharmony_ci 0, 336862306a36Sopenharmony_ci &msg, 336962306a36Sopenharmony_ci intf, 337062306a36Sopenharmony_ci NULL, 337162306a36Sopenharmony_ci NULL, 337262306a36Sopenharmony_ci 0, 337362306a36Sopenharmony_ci intf->addrinfo[0].address, 337462306a36Sopenharmony_ci intf->addrinfo[0].lun, 337562306a36Sopenharmony_ci -1, 0); 337662306a36Sopenharmony_ci} 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_cistatic void 337962306a36Sopenharmony_cichannel_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) 338062306a36Sopenharmony_ci{ 338162306a36Sopenharmony_ci int rv = 0; 338262306a36Sopenharmony_ci int ch; 338362306a36Sopenharmony_ci unsigned int set = intf->curr_working_cset; 338462306a36Sopenharmony_ci struct ipmi_channel *chans; 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 338762306a36Sopenharmony_ci && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) 338862306a36Sopenharmony_ci && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD)) { 338962306a36Sopenharmony_ci /* It's the one we want */ 339062306a36Sopenharmony_ci if (msg->msg.data[0] != 0) { 339162306a36Sopenharmony_ci /* Got an error from the channel, just go on. */ 339262306a36Sopenharmony_ci if (msg->msg.data[0] == IPMI_INVALID_COMMAND_ERR) { 339362306a36Sopenharmony_ci /* 339462306a36Sopenharmony_ci * If the MC does not support this 339562306a36Sopenharmony_ci * command, that is legal. We just 339662306a36Sopenharmony_ci * assume it has one IPMB at channel 339762306a36Sopenharmony_ci * zero. 339862306a36Sopenharmony_ci */ 339962306a36Sopenharmony_ci intf->wchannels[set].c[0].medium 340062306a36Sopenharmony_ci = IPMI_CHANNEL_MEDIUM_IPMB; 340162306a36Sopenharmony_ci intf->wchannels[set].c[0].protocol 340262306a36Sopenharmony_ci = IPMI_CHANNEL_PROTOCOL_IPMB; 340362306a36Sopenharmony_ci 340462306a36Sopenharmony_ci intf->channel_list = intf->wchannels + set; 340562306a36Sopenharmony_ci intf->channels_ready = true; 340662306a36Sopenharmony_ci wake_up(&intf->waitq); 340762306a36Sopenharmony_ci goto out; 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci goto next_channel; 341062306a36Sopenharmony_ci } 341162306a36Sopenharmony_ci if (msg->msg.data_len < 4) { 341262306a36Sopenharmony_ci /* Message not big enough, just go on. */ 341362306a36Sopenharmony_ci goto next_channel; 341462306a36Sopenharmony_ci } 341562306a36Sopenharmony_ci ch = intf->curr_channel; 341662306a36Sopenharmony_ci chans = intf->wchannels[set].c; 341762306a36Sopenharmony_ci chans[ch].medium = msg->msg.data[2] & 0x7f; 341862306a36Sopenharmony_ci chans[ch].protocol = msg->msg.data[3] & 0x1f; 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci next_channel: 342162306a36Sopenharmony_ci intf->curr_channel++; 342262306a36Sopenharmony_ci if (intf->curr_channel >= IPMI_MAX_CHANNELS) { 342362306a36Sopenharmony_ci intf->channel_list = intf->wchannels + set; 342462306a36Sopenharmony_ci intf->channels_ready = true; 342562306a36Sopenharmony_ci wake_up(&intf->waitq); 342662306a36Sopenharmony_ci } else { 342762306a36Sopenharmony_ci intf->channel_list = intf->wchannels + set; 342862306a36Sopenharmony_ci intf->channels_ready = true; 342962306a36Sopenharmony_ci rv = send_channel_info_cmd(intf, intf->curr_channel); 343062306a36Sopenharmony_ci } 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci if (rv) { 343362306a36Sopenharmony_ci /* Got an error somehow, just give up. */ 343462306a36Sopenharmony_ci dev_warn(intf->si_dev, 343562306a36Sopenharmony_ci "Error sending channel information for channel %d: %d\n", 343662306a36Sopenharmony_ci intf->curr_channel, rv); 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci intf->channel_list = intf->wchannels + set; 343962306a36Sopenharmony_ci intf->channels_ready = true; 344062306a36Sopenharmony_ci wake_up(&intf->waitq); 344162306a36Sopenharmony_ci } 344262306a36Sopenharmony_ci } 344362306a36Sopenharmony_ci out: 344462306a36Sopenharmony_ci return; 344562306a36Sopenharmony_ci} 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci/* 344862306a36Sopenharmony_ci * Must be holding intf->bmc_reg_mutex to call this. 344962306a36Sopenharmony_ci */ 345062306a36Sopenharmony_cistatic int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id) 345162306a36Sopenharmony_ci{ 345262306a36Sopenharmony_ci int rv; 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci if (ipmi_version_major(id) > 1 345562306a36Sopenharmony_ci || (ipmi_version_major(id) == 1 345662306a36Sopenharmony_ci && ipmi_version_minor(id) >= 5)) { 345762306a36Sopenharmony_ci unsigned int set; 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_ci /* 346062306a36Sopenharmony_ci * Start scanning the channels to see what is 346162306a36Sopenharmony_ci * available. 346262306a36Sopenharmony_ci */ 346362306a36Sopenharmony_ci set = !intf->curr_working_cset; 346462306a36Sopenharmony_ci intf->curr_working_cset = set; 346562306a36Sopenharmony_ci memset(&intf->wchannels[set], 0, 346662306a36Sopenharmony_ci sizeof(struct ipmi_channel_set)); 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci intf->null_user_handler = channel_handler; 346962306a36Sopenharmony_ci intf->curr_channel = 0; 347062306a36Sopenharmony_ci rv = send_channel_info_cmd(intf, 0); 347162306a36Sopenharmony_ci if (rv) { 347262306a36Sopenharmony_ci dev_warn(intf->si_dev, 347362306a36Sopenharmony_ci "Error sending channel information for channel 0, %d\n", 347462306a36Sopenharmony_ci rv); 347562306a36Sopenharmony_ci intf->null_user_handler = NULL; 347662306a36Sopenharmony_ci return -EIO; 347762306a36Sopenharmony_ci } 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci /* Wait for the channel info to be read. */ 348062306a36Sopenharmony_ci wait_event(intf->waitq, intf->channels_ready); 348162306a36Sopenharmony_ci intf->null_user_handler = NULL; 348262306a36Sopenharmony_ci } else { 348362306a36Sopenharmony_ci unsigned int set = intf->curr_working_cset; 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci /* Assume a single IPMB channel at zero. */ 348662306a36Sopenharmony_ci intf->wchannels[set].c[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; 348762306a36Sopenharmony_ci intf->wchannels[set].c[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; 348862306a36Sopenharmony_ci intf->channel_list = intf->wchannels + set; 348962306a36Sopenharmony_ci intf->channels_ready = true; 349062306a36Sopenharmony_ci } 349162306a36Sopenharmony_ci 349262306a36Sopenharmony_ci return 0; 349362306a36Sopenharmony_ci} 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_cistatic void ipmi_poll(struct ipmi_smi *intf) 349662306a36Sopenharmony_ci{ 349762306a36Sopenharmony_ci if (intf->handlers->poll) 349862306a36Sopenharmony_ci intf->handlers->poll(intf->send_info); 349962306a36Sopenharmony_ci /* In case something came in */ 350062306a36Sopenharmony_ci handle_new_recv_msgs(intf); 350162306a36Sopenharmony_ci} 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_civoid ipmi_poll_interface(struct ipmi_user *user) 350462306a36Sopenharmony_ci{ 350562306a36Sopenharmony_ci ipmi_poll(user->intf); 350662306a36Sopenharmony_ci} 350762306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_poll_interface); 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_cistatic ssize_t nr_users_show(struct device *dev, 351062306a36Sopenharmony_ci struct device_attribute *attr, 351162306a36Sopenharmony_ci char *buf) 351262306a36Sopenharmony_ci{ 351362306a36Sopenharmony_ci struct ipmi_smi *intf = container_of(attr, 351462306a36Sopenharmony_ci struct ipmi_smi, nr_users_devattr); 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", atomic_read(&intf->nr_users)); 351762306a36Sopenharmony_ci} 351862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(nr_users); 351962306a36Sopenharmony_ci 352062306a36Sopenharmony_cistatic ssize_t nr_msgs_show(struct device *dev, 352162306a36Sopenharmony_ci struct device_attribute *attr, 352262306a36Sopenharmony_ci char *buf) 352362306a36Sopenharmony_ci{ 352462306a36Sopenharmony_ci struct ipmi_smi *intf = container_of(attr, 352562306a36Sopenharmony_ci struct ipmi_smi, nr_msgs_devattr); 352662306a36Sopenharmony_ci struct ipmi_user *user; 352762306a36Sopenharmony_ci int index; 352862306a36Sopenharmony_ci unsigned int count = 0; 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci index = srcu_read_lock(&intf->users_srcu); 353162306a36Sopenharmony_ci list_for_each_entry_rcu(user, &intf->users, link) 353262306a36Sopenharmony_ci count += atomic_read(&user->nr_msgs); 353362306a36Sopenharmony_ci srcu_read_unlock(&intf->users_srcu, index); 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", count); 353662306a36Sopenharmony_ci} 353762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(nr_msgs); 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_cistatic void redo_bmc_reg(struct work_struct *work) 354062306a36Sopenharmony_ci{ 354162306a36Sopenharmony_ci struct ipmi_smi *intf = container_of(work, struct ipmi_smi, 354262306a36Sopenharmony_ci bmc_reg_work); 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci if (!intf->in_shutdown) 354562306a36Sopenharmony_ci bmc_get_device_id(intf, NULL, NULL, NULL, NULL); 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci kref_put(&intf->refcount, intf_free); 354862306a36Sopenharmony_ci} 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ciint ipmi_add_smi(struct module *owner, 355162306a36Sopenharmony_ci const struct ipmi_smi_handlers *handlers, 355262306a36Sopenharmony_ci void *send_info, 355362306a36Sopenharmony_ci struct device *si_dev, 355462306a36Sopenharmony_ci unsigned char slave_addr) 355562306a36Sopenharmony_ci{ 355662306a36Sopenharmony_ci int i, j; 355762306a36Sopenharmony_ci int rv; 355862306a36Sopenharmony_ci struct ipmi_smi *intf, *tintf; 355962306a36Sopenharmony_ci struct list_head *link; 356062306a36Sopenharmony_ci struct ipmi_device_id id; 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_ci /* 356362306a36Sopenharmony_ci * Make sure the driver is actually initialized, this handles 356462306a36Sopenharmony_ci * problems with initialization order. 356562306a36Sopenharmony_ci */ 356662306a36Sopenharmony_ci rv = ipmi_init_msghandler(); 356762306a36Sopenharmony_ci if (rv) 356862306a36Sopenharmony_ci return rv; 356962306a36Sopenharmony_ci 357062306a36Sopenharmony_ci intf = kzalloc(sizeof(*intf), GFP_KERNEL); 357162306a36Sopenharmony_ci if (!intf) 357262306a36Sopenharmony_ci return -ENOMEM; 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci rv = init_srcu_struct(&intf->users_srcu); 357562306a36Sopenharmony_ci if (rv) { 357662306a36Sopenharmony_ci kfree(intf); 357762306a36Sopenharmony_ci return rv; 357862306a36Sopenharmony_ci } 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci intf->owner = owner; 358162306a36Sopenharmony_ci intf->bmc = &intf->tmp_bmc; 358262306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->bmc->intfs); 358362306a36Sopenharmony_ci mutex_init(&intf->bmc->dyn_mutex); 358462306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->bmc_link); 358562306a36Sopenharmony_ci mutex_init(&intf->bmc_reg_mutex); 358662306a36Sopenharmony_ci intf->intf_num = -1; /* Mark it invalid for now. */ 358762306a36Sopenharmony_ci kref_init(&intf->refcount); 358862306a36Sopenharmony_ci INIT_WORK(&intf->bmc_reg_work, redo_bmc_reg); 358962306a36Sopenharmony_ci intf->si_dev = si_dev; 359062306a36Sopenharmony_ci for (j = 0; j < IPMI_MAX_CHANNELS; j++) { 359162306a36Sopenharmony_ci intf->addrinfo[j].address = IPMI_BMC_SLAVE_ADDR; 359262306a36Sopenharmony_ci intf->addrinfo[j].lun = 2; 359362306a36Sopenharmony_ci } 359462306a36Sopenharmony_ci if (slave_addr != 0) 359562306a36Sopenharmony_ci intf->addrinfo[0].address = slave_addr; 359662306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->users); 359762306a36Sopenharmony_ci atomic_set(&intf->nr_users, 0); 359862306a36Sopenharmony_ci intf->handlers = handlers; 359962306a36Sopenharmony_ci intf->send_info = send_info; 360062306a36Sopenharmony_ci spin_lock_init(&intf->seq_lock); 360162306a36Sopenharmony_ci for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) { 360262306a36Sopenharmony_ci intf->seq_table[j].inuse = 0; 360362306a36Sopenharmony_ci intf->seq_table[j].seqid = 0; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci intf->curr_seq = 0; 360662306a36Sopenharmony_ci spin_lock_init(&intf->waiting_rcv_msgs_lock); 360762306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->waiting_rcv_msgs); 360862306a36Sopenharmony_ci tasklet_setup(&intf->recv_tasklet, 360962306a36Sopenharmony_ci smi_recv_tasklet); 361062306a36Sopenharmony_ci atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0); 361162306a36Sopenharmony_ci spin_lock_init(&intf->xmit_msgs_lock); 361262306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->xmit_msgs); 361362306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->hp_xmit_msgs); 361462306a36Sopenharmony_ci spin_lock_init(&intf->events_lock); 361562306a36Sopenharmony_ci spin_lock_init(&intf->watch_lock); 361662306a36Sopenharmony_ci atomic_set(&intf->event_waiters, 0); 361762306a36Sopenharmony_ci intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; 361862306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->waiting_events); 361962306a36Sopenharmony_ci intf->waiting_events_count = 0; 362062306a36Sopenharmony_ci mutex_init(&intf->cmd_rcvrs_mutex); 362162306a36Sopenharmony_ci spin_lock_init(&intf->maintenance_mode_lock); 362262306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->cmd_rcvrs); 362362306a36Sopenharmony_ci init_waitqueue_head(&intf->waitq); 362462306a36Sopenharmony_ci for (i = 0; i < IPMI_NUM_STATS; i++) 362562306a36Sopenharmony_ci atomic_set(&intf->stats[i], 0); 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci mutex_lock(&ipmi_interfaces_mutex); 362862306a36Sopenharmony_ci /* Look for a hole in the numbers. */ 362962306a36Sopenharmony_ci i = 0; 363062306a36Sopenharmony_ci link = &ipmi_interfaces; 363162306a36Sopenharmony_ci list_for_each_entry_rcu(tintf, &ipmi_interfaces, link, 363262306a36Sopenharmony_ci ipmi_interfaces_mutex_held()) { 363362306a36Sopenharmony_ci if (tintf->intf_num != i) { 363462306a36Sopenharmony_ci link = &tintf->link; 363562306a36Sopenharmony_ci break; 363662306a36Sopenharmony_ci } 363762306a36Sopenharmony_ci i++; 363862306a36Sopenharmony_ci } 363962306a36Sopenharmony_ci /* Add the new interface in numeric order. */ 364062306a36Sopenharmony_ci if (i == 0) 364162306a36Sopenharmony_ci list_add_rcu(&intf->link, &ipmi_interfaces); 364262306a36Sopenharmony_ci else 364362306a36Sopenharmony_ci list_add_tail_rcu(&intf->link, link); 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci rv = handlers->start_processing(send_info, intf); 364662306a36Sopenharmony_ci if (rv) 364762306a36Sopenharmony_ci goto out_err; 364862306a36Sopenharmony_ci 364962306a36Sopenharmony_ci rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i); 365062306a36Sopenharmony_ci if (rv) { 365162306a36Sopenharmony_ci dev_err(si_dev, "Unable to get the device id: %d\n", rv); 365262306a36Sopenharmony_ci goto out_err_started; 365362306a36Sopenharmony_ci } 365462306a36Sopenharmony_ci 365562306a36Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 365662306a36Sopenharmony_ci rv = __scan_channels(intf, &id); 365762306a36Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 365862306a36Sopenharmony_ci if (rv) 365962306a36Sopenharmony_ci goto out_err_bmc_reg; 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci intf->nr_users_devattr = dev_attr_nr_users; 366262306a36Sopenharmony_ci sysfs_attr_init(&intf->nr_users_devattr.attr); 366362306a36Sopenharmony_ci rv = device_create_file(intf->si_dev, &intf->nr_users_devattr); 366462306a36Sopenharmony_ci if (rv) 366562306a36Sopenharmony_ci goto out_err_bmc_reg; 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci intf->nr_msgs_devattr = dev_attr_nr_msgs; 366862306a36Sopenharmony_ci sysfs_attr_init(&intf->nr_msgs_devattr.attr); 366962306a36Sopenharmony_ci rv = device_create_file(intf->si_dev, &intf->nr_msgs_devattr); 367062306a36Sopenharmony_ci if (rv) { 367162306a36Sopenharmony_ci device_remove_file(intf->si_dev, &intf->nr_users_devattr); 367262306a36Sopenharmony_ci goto out_err_bmc_reg; 367362306a36Sopenharmony_ci } 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_ci /* 367662306a36Sopenharmony_ci * Keep memory order straight for RCU readers. Make 367762306a36Sopenharmony_ci * sure everything else is committed to memory before 367862306a36Sopenharmony_ci * setting intf_num to mark the interface valid. 367962306a36Sopenharmony_ci */ 368062306a36Sopenharmony_ci smp_wmb(); 368162306a36Sopenharmony_ci intf->intf_num = i; 368262306a36Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 368362306a36Sopenharmony_ci 368462306a36Sopenharmony_ci /* After this point the interface is legal to use. */ 368562306a36Sopenharmony_ci call_smi_watchers(i, intf->si_dev); 368662306a36Sopenharmony_ci 368762306a36Sopenharmony_ci return 0; 368862306a36Sopenharmony_ci 368962306a36Sopenharmony_ci out_err_bmc_reg: 369062306a36Sopenharmony_ci ipmi_bmc_unregister(intf); 369162306a36Sopenharmony_ci out_err_started: 369262306a36Sopenharmony_ci if (intf->handlers->shutdown) 369362306a36Sopenharmony_ci intf->handlers->shutdown(intf->send_info); 369462306a36Sopenharmony_ci out_err: 369562306a36Sopenharmony_ci list_del_rcu(&intf->link); 369662306a36Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 369762306a36Sopenharmony_ci synchronize_srcu(&ipmi_interfaces_srcu); 369862306a36Sopenharmony_ci cleanup_srcu_struct(&intf->users_srcu); 369962306a36Sopenharmony_ci kref_put(&intf->refcount, intf_free); 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci return rv; 370262306a36Sopenharmony_ci} 370362306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_add_smi); 370462306a36Sopenharmony_ci 370562306a36Sopenharmony_cistatic void deliver_smi_err_response(struct ipmi_smi *intf, 370662306a36Sopenharmony_ci struct ipmi_smi_msg *msg, 370762306a36Sopenharmony_ci unsigned char err) 370862306a36Sopenharmony_ci{ 370962306a36Sopenharmony_ci int rv; 371062306a36Sopenharmony_ci msg->rsp[0] = msg->data[0] | 4; 371162306a36Sopenharmony_ci msg->rsp[1] = msg->data[1]; 371262306a36Sopenharmony_ci msg->rsp[2] = err; 371362306a36Sopenharmony_ci msg->rsp_size = 3; 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci /* This will never requeue, but it may ask us to free the message. */ 371662306a36Sopenharmony_ci rv = handle_one_recv_msg(intf, msg); 371762306a36Sopenharmony_ci if (rv == 0) 371862306a36Sopenharmony_ci ipmi_free_smi_msg(msg); 371962306a36Sopenharmony_ci} 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_cistatic void cleanup_smi_msgs(struct ipmi_smi *intf) 372262306a36Sopenharmony_ci{ 372362306a36Sopenharmony_ci int i; 372462306a36Sopenharmony_ci struct seq_table *ent; 372562306a36Sopenharmony_ci struct ipmi_smi_msg *msg; 372662306a36Sopenharmony_ci struct list_head *entry; 372762306a36Sopenharmony_ci struct list_head tmplist; 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci /* Clear out our transmit queues and hold the messages. */ 373062306a36Sopenharmony_ci INIT_LIST_HEAD(&tmplist); 373162306a36Sopenharmony_ci list_splice_tail(&intf->hp_xmit_msgs, &tmplist); 373262306a36Sopenharmony_ci list_splice_tail(&intf->xmit_msgs, &tmplist); 373362306a36Sopenharmony_ci 373462306a36Sopenharmony_ci /* Current message first, to preserve order */ 373562306a36Sopenharmony_ci while (intf->curr_msg && !list_empty(&intf->waiting_rcv_msgs)) { 373662306a36Sopenharmony_ci /* Wait for the message to clear out. */ 373762306a36Sopenharmony_ci schedule_timeout(1); 373862306a36Sopenharmony_ci } 373962306a36Sopenharmony_ci 374062306a36Sopenharmony_ci /* No need for locks, the interface is down. */ 374162306a36Sopenharmony_ci 374262306a36Sopenharmony_ci /* 374362306a36Sopenharmony_ci * Return errors for all pending messages in queue and in the 374462306a36Sopenharmony_ci * tables waiting for remote responses. 374562306a36Sopenharmony_ci */ 374662306a36Sopenharmony_ci while (!list_empty(&tmplist)) { 374762306a36Sopenharmony_ci entry = tmplist.next; 374862306a36Sopenharmony_ci list_del(entry); 374962306a36Sopenharmony_ci msg = list_entry(entry, struct ipmi_smi_msg, link); 375062306a36Sopenharmony_ci deliver_smi_err_response(intf, msg, IPMI_ERR_UNSPECIFIED); 375162306a36Sopenharmony_ci } 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_ci for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { 375462306a36Sopenharmony_ci ent = &intf->seq_table[i]; 375562306a36Sopenharmony_ci if (!ent->inuse) 375662306a36Sopenharmony_ci continue; 375762306a36Sopenharmony_ci deliver_err_response(intf, ent->recv_msg, IPMI_ERR_UNSPECIFIED); 375862306a36Sopenharmony_ci } 375962306a36Sopenharmony_ci} 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_civoid ipmi_unregister_smi(struct ipmi_smi *intf) 376262306a36Sopenharmony_ci{ 376362306a36Sopenharmony_ci struct ipmi_smi_watcher *w; 376462306a36Sopenharmony_ci int intf_num, index; 376562306a36Sopenharmony_ci 376662306a36Sopenharmony_ci if (!intf) 376762306a36Sopenharmony_ci return; 376862306a36Sopenharmony_ci intf_num = intf->intf_num; 376962306a36Sopenharmony_ci mutex_lock(&ipmi_interfaces_mutex); 377062306a36Sopenharmony_ci intf->intf_num = -1; 377162306a36Sopenharmony_ci intf->in_shutdown = true; 377262306a36Sopenharmony_ci list_del_rcu(&intf->link); 377362306a36Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 377462306a36Sopenharmony_ci synchronize_srcu(&ipmi_interfaces_srcu); 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_ci /* At this point no users can be added to the interface. */ 377762306a36Sopenharmony_ci 377862306a36Sopenharmony_ci device_remove_file(intf->si_dev, &intf->nr_msgs_devattr); 377962306a36Sopenharmony_ci device_remove_file(intf->si_dev, &intf->nr_users_devattr); 378062306a36Sopenharmony_ci 378162306a36Sopenharmony_ci /* 378262306a36Sopenharmony_ci * Call all the watcher interfaces to tell them that 378362306a36Sopenharmony_ci * an interface is going away. 378462306a36Sopenharmony_ci */ 378562306a36Sopenharmony_ci mutex_lock(&smi_watchers_mutex); 378662306a36Sopenharmony_ci list_for_each_entry(w, &smi_watchers, link) 378762306a36Sopenharmony_ci w->smi_gone(intf_num); 378862306a36Sopenharmony_ci mutex_unlock(&smi_watchers_mutex); 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_ci index = srcu_read_lock(&intf->users_srcu); 379162306a36Sopenharmony_ci while (!list_empty(&intf->users)) { 379262306a36Sopenharmony_ci struct ipmi_user *user = 379362306a36Sopenharmony_ci container_of(list_next_rcu(&intf->users), 379462306a36Sopenharmony_ci struct ipmi_user, link); 379562306a36Sopenharmony_ci 379662306a36Sopenharmony_ci _ipmi_destroy_user(user); 379762306a36Sopenharmony_ci } 379862306a36Sopenharmony_ci srcu_read_unlock(&intf->users_srcu, index); 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ci if (intf->handlers->shutdown) 380162306a36Sopenharmony_ci intf->handlers->shutdown(intf->send_info); 380262306a36Sopenharmony_ci 380362306a36Sopenharmony_ci cleanup_smi_msgs(intf); 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci ipmi_bmc_unregister(intf); 380662306a36Sopenharmony_ci 380762306a36Sopenharmony_ci cleanup_srcu_struct(&intf->users_srcu); 380862306a36Sopenharmony_ci kref_put(&intf->refcount, intf_free); 380962306a36Sopenharmony_ci} 381062306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_unregister_smi); 381162306a36Sopenharmony_ci 381262306a36Sopenharmony_cistatic int handle_ipmb_get_msg_rsp(struct ipmi_smi *intf, 381362306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 381462306a36Sopenharmony_ci{ 381562306a36Sopenharmony_ci struct ipmi_ipmb_addr ipmb_addr; 381662306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 381762306a36Sopenharmony_ci 381862306a36Sopenharmony_ci /* 381962306a36Sopenharmony_ci * This is 11, not 10, because the response must contain a 382062306a36Sopenharmony_ci * completion code. 382162306a36Sopenharmony_ci */ 382262306a36Sopenharmony_ci if (msg->rsp_size < 11) { 382362306a36Sopenharmony_ci /* Message not big enough, just ignore it. */ 382462306a36Sopenharmony_ci ipmi_inc_stat(intf, invalid_ipmb_responses); 382562306a36Sopenharmony_ci return 0; 382662306a36Sopenharmony_ci } 382762306a36Sopenharmony_ci 382862306a36Sopenharmony_ci if (msg->rsp[2] != 0) { 382962306a36Sopenharmony_ci /* An error getting the response, just ignore it. */ 383062306a36Sopenharmony_ci return 0; 383162306a36Sopenharmony_ci } 383262306a36Sopenharmony_ci 383362306a36Sopenharmony_ci ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; 383462306a36Sopenharmony_ci ipmb_addr.slave_addr = msg->rsp[6]; 383562306a36Sopenharmony_ci ipmb_addr.channel = msg->rsp[3] & 0x0f; 383662306a36Sopenharmony_ci ipmb_addr.lun = msg->rsp[7] & 3; 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci /* 383962306a36Sopenharmony_ci * It's a response from a remote entity. Look up the sequence 384062306a36Sopenharmony_ci * number and handle the response. 384162306a36Sopenharmony_ci */ 384262306a36Sopenharmony_ci if (intf_find_seq(intf, 384362306a36Sopenharmony_ci msg->rsp[7] >> 2, 384462306a36Sopenharmony_ci msg->rsp[3] & 0x0f, 384562306a36Sopenharmony_ci msg->rsp[8], 384662306a36Sopenharmony_ci (msg->rsp[4] >> 2) & (~1), 384762306a36Sopenharmony_ci (struct ipmi_addr *) &ipmb_addr, 384862306a36Sopenharmony_ci &recv_msg)) { 384962306a36Sopenharmony_ci /* 385062306a36Sopenharmony_ci * We were unable to find the sequence number, 385162306a36Sopenharmony_ci * so just nuke the message. 385262306a36Sopenharmony_ci */ 385362306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_ipmb_responses); 385462306a36Sopenharmony_ci return 0; 385562306a36Sopenharmony_ci } 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[9], msg->rsp_size - 9); 385862306a36Sopenharmony_ci /* 385962306a36Sopenharmony_ci * The other fields matched, so no need to set them, except 386062306a36Sopenharmony_ci * for netfn, which needs to be the response that was 386162306a36Sopenharmony_ci * returned, not the request value. 386262306a36Sopenharmony_ci */ 386362306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[4] >> 2; 386462306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 386562306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 10; 386662306a36Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 386762306a36Sopenharmony_ci if (deliver_response(intf, recv_msg)) 386862306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_ipmb_responses); 386962306a36Sopenharmony_ci else 387062306a36Sopenharmony_ci ipmi_inc_stat(intf, handled_ipmb_responses); 387162306a36Sopenharmony_ci 387262306a36Sopenharmony_ci return 0; 387362306a36Sopenharmony_ci} 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_cistatic int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, 387662306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 387762306a36Sopenharmony_ci{ 387862306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 387962306a36Sopenharmony_ci int rv = 0; 388062306a36Sopenharmony_ci unsigned char netfn; 388162306a36Sopenharmony_ci unsigned char cmd; 388262306a36Sopenharmony_ci unsigned char chan; 388362306a36Sopenharmony_ci struct ipmi_user *user = NULL; 388462306a36Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr; 388562306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 388662306a36Sopenharmony_ci 388762306a36Sopenharmony_ci if (msg->rsp_size < 10) { 388862306a36Sopenharmony_ci /* Message not big enough, just ignore it. */ 388962306a36Sopenharmony_ci ipmi_inc_stat(intf, invalid_commands); 389062306a36Sopenharmony_ci return 0; 389162306a36Sopenharmony_ci } 389262306a36Sopenharmony_ci 389362306a36Sopenharmony_ci if (msg->rsp[2] != 0) { 389462306a36Sopenharmony_ci /* An error getting the response, just ignore it. */ 389562306a36Sopenharmony_ci return 0; 389662306a36Sopenharmony_ci } 389762306a36Sopenharmony_ci 389862306a36Sopenharmony_ci netfn = msg->rsp[4] >> 2; 389962306a36Sopenharmony_ci cmd = msg->rsp[8]; 390062306a36Sopenharmony_ci chan = msg->rsp[3] & 0xf; 390162306a36Sopenharmony_ci 390262306a36Sopenharmony_ci rcu_read_lock(); 390362306a36Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); 390462306a36Sopenharmony_ci if (rcvr) { 390562306a36Sopenharmony_ci user = rcvr->user; 390662306a36Sopenharmony_ci kref_get(&user->refcount); 390762306a36Sopenharmony_ci } else 390862306a36Sopenharmony_ci user = NULL; 390962306a36Sopenharmony_ci rcu_read_unlock(); 391062306a36Sopenharmony_ci 391162306a36Sopenharmony_ci if (user == NULL) { 391262306a36Sopenharmony_ci /* We didn't find a user, deliver an error response. */ 391362306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_ci msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 391662306a36Sopenharmony_ci msg->data[1] = IPMI_SEND_MSG_CMD; 391762306a36Sopenharmony_ci msg->data[2] = msg->rsp[3]; 391862306a36Sopenharmony_ci msg->data[3] = msg->rsp[6]; 391962306a36Sopenharmony_ci msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); 392062306a36Sopenharmony_ci msg->data[5] = ipmb_checksum(&msg->data[3], 2); 392162306a36Sopenharmony_ci msg->data[6] = intf->addrinfo[msg->rsp[3] & 0xf].address; 392262306a36Sopenharmony_ci /* rqseq/lun */ 392362306a36Sopenharmony_ci msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); 392462306a36Sopenharmony_ci msg->data[8] = msg->rsp[8]; /* cmd */ 392562306a36Sopenharmony_ci msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE; 392662306a36Sopenharmony_ci msg->data[10] = ipmb_checksum(&msg->data[6], 4); 392762306a36Sopenharmony_ci msg->data_size = 11; 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci dev_dbg(intf->si_dev, "Invalid command: %*ph\n", 393062306a36Sopenharmony_ci msg->data_size, msg->data); 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci rcu_read_lock(); 393362306a36Sopenharmony_ci if (!intf->in_shutdown) { 393462306a36Sopenharmony_ci smi_send(intf, intf->handlers, msg, 0); 393562306a36Sopenharmony_ci /* 393662306a36Sopenharmony_ci * We used the message, so return the value 393762306a36Sopenharmony_ci * that causes it to not be freed or 393862306a36Sopenharmony_ci * queued. 393962306a36Sopenharmony_ci */ 394062306a36Sopenharmony_ci rv = -1; 394162306a36Sopenharmony_ci } 394262306a36Sopenharmony_ci rcu_read_unlock(); 394362306a36Sopenharmony_ci } else { 394462306a36Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 394562306a36Sopenharmony_ci if (!recv_msg) { 394662306a36Sopenharmony_ci /* 394762306a36Sopenharmony_ci * We couldn't allocate memory for the 394862306a36Sopenharmony_ci * message, so requeue it for handling 394962306a36Sopenharmony_ci * later. 395062306a36Sopenharmony_ci */ 395162306a36Sopenharmony_ci rv = 1; 395262306a36Sopenharmony_ci kref_put(&user->refcount, free_user); 395362306a36Sopenharmony_ci } else { 395462306a36Sopenharmony_ci /* Extract the source address from the data. */ 395562306a36Sopenharmony_ci ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr; 395662306a36Sopenharmony_ci ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE; 395762306a36Sopenharmony_ci ipmb_addr->slave_addr = msg->rsp[6]; 395862306a36Sopenharmony_ci ipmb_addr->lun = msg->rsp[7] & 3; 395962306a36Sopenharmony_ci ipmb_addr->channel = msg->rsp[3] & 0xf; 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_ci /* 396262306a36Sopenharmony_ci * Extract the rest of the message information 396362306a36Sopenharmony_ci * from the IPMB header. 396462306a36Sopenharmony_ci */ 396562306a36Sopenharmony_ci recv_msg->user = user; 396662306a36Sopenharmony_ci recv_msg->recv_type = IPMI_CMD_RECV_TYPE; 396762306a36Sopenharmony_ci recv_msg->msgid = msg->rsp[7] >> 2; 396862306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[4] >> 2; 396962306a36Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[8]; 397062306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 397162306a36Sopenharmony_ci 397262306a36Sopenharmony_ci /* 397362306a36Sopenharmony_ci * We chop off 10, not 9 bytes because the checksum 397462306a36Sopenharmony_ci * at the end also needs to be removed. 397562306a36Sopenharmony_ci */ 397662306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 10; 397762306a36Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[9], 397862306a36Sopenharmony_ci msg->rsp_size - 10); 397962306a36Sopenharmony_ci if (deliver_response(intf, recv_msg)) 398062306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 398162306a36Sopenharmony_ci else 398262306a36Sopenharmony_ci ipmi_inc_stat(intf, handled_commands); 398362306a36Sopenharmony_ci } 398462306a36Sopenharmony_ci } 398562306a36Sopenharmony_ci 398662306a36Sopenharmony_ci return rv; 398762306a36Sopenharmony_ci} 398862306a36Sopenharmony_ci 398962306a36Sopenharmony_cistatic int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf, 399062306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 399162306a36Sopenharmony_ci{ 399262306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 399362306a36Sopenharmony_ci int rv = 0; 399462306a36Sopenharmony_ci struct ipmi_user *user = NULL; 399562306a36Sopenharmony_ci struct ipmi_ipmb_direct_addr *daddr; 399662306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 399762306a36Sopenharmony_ci unsigned char netfn = msg->rsp[0] >> 2; 399862306a36Sopenharmony_ci unsigned char cmd = msg->rsp[3]; 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_ci rcu_read_lock(); 400162306a36Sopenharmony_ci /* We always use channel 0 for direct messages. */ 400262306a36Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, 0); 400362306a36Sopenharmony_ci if (rcvr) { 400462306a36Sopenharmony_ci user = rcvr->user; 400562306a36Sopenharmony_ci kref_get(&user->refcount); 400662306a36Sopenharmony_ci } else 400762306a36Sopenharmony_ci user = NULL; 400862306a36Sopenharmony_ci rcu_read_unlock(); 400962306a36Sopenharmony_ci 401062306a36Sopenharmony_ci if (user == NULL) { 401162306a36Sopenharmony_ci /* We didn't find a user, deliver an error response. */ 401262306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 401362306a36Sopenharmony_ci 401462306a36Sopenharmony_ci msg->data[0] = (netfn + 1) << 2; 401562306a36Sopenharmony_ci msg->data[0] |= msg->rsp[2] & 0x3; /* rqLUN */ 401662306a36Sopenharmony_ci msg->data[1] = msg->rsp[1]; /* Addr */ 401762306a36Sopenharmony_ci msg->data[2] = msg->rsp[2] & ~0x3; /* rqSeq */ 401862306a36Sopenharmony_ci msg->data[2] |= msg->rsp[0] & 0x3; /* rsLUN */ 401962306a36Sopenharmony_ci msg->data[3] = cmd; 402062306a36Sopenharmony_ci msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE; 402162306a36Sopenharmony_ci msg->data_size = 5; 402262306a36Sopenharmony_ci 402362306a36Sopenharmony_ci rcu_read_lock(); 402462306a36Sopenharmony_ci if (!intf->in_shutdown) { 402562306a36Sopenharmony_ci smi_send(intf, intf->handlers, msg, 0); 402662306a36Sopenharmony_ci /* 402762306a36Sopenharmony_ci * We used the message, so return the value 402862306a36Sopenharmony_ci * that causes it to not be freed or 402962306a36Sopenharmony_ci * queued. 403062306a36Sopenharmony_ci */ 403162306a36Sopenharmony_ci rv = -1; 403262306a36Sopenharmony_ci } 403362306a36Sopenharmony_ci rcu_read_unlock(); 403462306a36Sopenharmony_ci } else { 403562306a36Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 403662306a36Sopenharmony_ci if (!recv_msg) { 403762306a36Sopenharmony_ci /* 403862306a36Sopenharmony_ci * We couldn't allocate memory for the 403962306a36Sopenharmony_ci * message, so requeue it for handling 404062306a36Sopenharmony_ci * later. 404162306a36Sopenharmony_ci */ 404262306a36Sopenharmony_ci rv = 1; 404362306a36Sopenharmony_ci kref_put(&user->refcount, free_user); 404462306a36Sopenharmony_ci } else { 404562306a36Sopenharmony_ci /* Extract the source address from the data. */ 404662306a36Sopenharmony_ci daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr; 404762306a36Sopenharmony_ci daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE; 404862306a36Sopenharmony_ci daddr->channel = 0; 404962306a36Sopenharmony_ci daddr->slave_addr = msg->rsp[1]; 405062306a36Sopenharmony_ci daddr->rs_lun = msg->rsp[0] & 3; 405162306a36Sopenharmony_ci daddr->rq_lun = msg->rsp[2] & 3; 405262306a36Sopenharmony_ci 405362306a36Sopenharmony_ci /* 405462306a36Sopenharmony_ci * Extract the rest of the message information 405562306a36Sopenharmony_ci * from the IPMB header. 405662306a36Sopenharmony_ci */ 405762306a36Sopenharmony_ci recv_msg->user = user; 405862306a36Sopenharmony_ci recv_msg->recv_type = IPMI_CMD_RECV_TYPE; 405962306a36Sopenharmony_ci recv_msg->msgid = (msg->rsp[2] >> 2); 406062306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[0] >> 2; 406162306a36Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[3]; 406262306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 406362306a36Sopenharmony_ci 406462306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 4; 406562306a36Sopenharmony_ci memcpy(recv_msg->msg_data, msg->rsp + 4, 406662306a36Sopenharmony_ci msg->rsp_size - 4); 406762306a36Sopenharmony_ci if (deliver_response(intf, recv_msg)) 406862306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 406962306a36Sopenharmony_ci else 407062306a36Sopenharmony_ci ipmi_inc_stat(intf, handled_commands); 407162306a36Sopenharmony_ci } 407262306a36Sopenharmony_ci } 407362306a36Sopenharmony_ci 407462306a36Sopenharmony_ci return rv; 407562306a36Sopenharmony_ci} 407662306a36Sopenharmony_ci 407762306a36Sopenharmony_cistatic int handle_ipmb_direct_rcv_rsp(struct ipmi_smi *intf, 407862306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 407962306a36Sopenharmony_ci{ 408062306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 408162306a36Sopenharmony_ci struct ipmi_ipmb_direct_addr *daddr; 408262306a36Sopenharmony_ci 408362306a36Sopenharmony_ci recv_msg = msg->user_data; 408462306a36Sopenharmony_ci if (recv_msg == NULL) { 408562306a36Sopenharmony_ci dev_warn(intf->si_dev, 408662306a36Sopenharmony_ci "IPMI direct message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n"); 408762306a36Sopenharmony_ci return 0; 408862306a36Sopenharmony_ci } 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 409162306a36Sopenharmony_ci recv_msg->msgid = msg->msgid; 409262306a36Sopenharmony_ci daddr = (struct ipmi_ipmb_direct_addr *) &recv_msg->addr; 409362306a36Sopenharmony_ci daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE; 409462306a36Sopenharmony_ci daddr->channel = 0; 409562306a36Sopenharmony_ci daddr->slave_addr = msg->rsp[1]; 409662306a36Sopenharmony_ci daddr->rq_lun = msg->rsp[0] & 3; 409762306a36Sopenharmony_ci daddr->rs_lun = msg->rsp[2] & 3; 409862306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[0] >> 2; 409962306a36Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[3]; 410062306a36Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[4], msg->rsp_size - 4); 410162306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 410262306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 4; 410362306a36Sopenharmony_ci deliver_local_response(intf, recv_msg); 410462306a36Sopenharmony_ci 410562306a36Sopenharmony_ci return 0; 410662306a36Sopenharmony_ci} 410762306a36Sopenharmony_ci 410862306a36Sopenharmony_cistatic int handle_lan_get_msg_rsp(struct ipmi_smi *intf, 410962306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 411062306a36Sopenharmony_ci{ 411162306a36Sopenharmony_ci struct ipmi_lan_addr lan_addr; 411262306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_ci 411562306a36Sopenharmony_ci /* 411662306a36Sopenharmony_ci * This is 13, not 12, because the response must contain a 411762306a36Sopenharmony_ci * completion code. 411862306a36Sopenharmony_ci */ 411962306a36Sopenharmony_ci if (msg->rsp_size < 13) { 412062306a36Sopenharmony_ci /* Message not big enough, just ignore it. */ 412162306a36Sopenharmony_ci ipmi_inc_stat(intf, invalid_lan_responses); 412262306a36Sopenharmony_ci return 0; 412362306a36Sopenharmony_ci } 412462306a36Sopenharmony_ci 412562306a36Sopenharmony_ci if (msg->rsp[2] != 0) { 412662306a36Sopenharmony_ci /* An error getting the response, just ignore it. */ 412762306a36Sopenharmony_ci return 0; 412862306a36Sopenharmony_ci } 412962306a36Sopenharmony_ci 413062306a36Sopenharmony_ci lan_addr.addr_type = IPMI_LAN_ADDR_TYPE; 413162306a36Sopenharmony_ci lan_addr.session_handle = msg->rsp[4]; 413262306a36Sopenharmony_ci lan_addr.remote_SWID = msg->rsp[8]; 413362306a36Sopenharmony_ci lan_addr.local_SWID = msg->rsp[5]; 413462306a36Sopenharmony_ci lan_addr.channel = msg->rsp[3] & 0x0f; 413562306a36Sopenharmony_ci lan_addr.privilege = msg->rsp[3] >> 4; 413662306a36Sopenharmony_ci lan_addr.lun = msg->rsp[9] & 3; 413762306a36Sopenharmony_ci 413862306a36Sopenharmony_ci /* 413962306a36Sopenharmony_ci * It's a response from a remote entity. Look up the sequence 414062306a36Sopenharmony_ci * number and handle the response. 414162306a36Sopenharmony_ci */ 414262306a36Sopenharmony_ci if (intf_find_seq(intf, 414362306a36Sopenharmony_ci msg->rsp[9] >> 2, 414462306a36Sopenharmony_ci msg->rsp[3] & 0x0f, 414562306a36Sopenharmony_ci msg->rsp[10], 414662306a36Sopenharmony_ci (msg->rsp[6] >> 2) & (~1), 414762306a36Sopenharmony_ci (struct ipmi_addr *) &lan_addr, 414862306a36Sopenharmony_ci &recv_msg)) { 414962306a36Sopenharmony_ci /* 415062306a36Sopenharmony_ci * We were unable to find the sequence number, 415162306a36Sopenharmony_ci * so just nuke the message. 415262306a36Sopenharmony_ci */ 415362306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_lan_responses); 415462306a36Sopenharmony_ci return 0; 415562306a36Sopenharmony_ci } 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[11], msg->rsp_size - 11); 415862306a36Sopenharmony_ci /* 415962306a36Sopenharmony_ci * The other fields matched, so no need to set them, except 416062306a36Sopenharmony_ci * for netfn, which needs to be the response that was 416162306a36Sopenharmony_ci * returned, not the request value. 416262306a36Sopenharmony_ci */ 416362306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[6] >> 2; 416462306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 416562306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 12; 416662306a36Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 416762306a36Sopenharmony_ci if (deliver_response(intf, recv_msg)) 416862306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_lan_responses); 416962306a36Sopenharmony_ci else 417062306a36Sopenharmony_ci ipmi_inc_stat(intf, handled_lan_responses); 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_ci return 0; 417362306a36Sopenharmony_ci} 417462306a36Sopenharmony_ci 417562306a36Sopenharmony_cistatic int handle_lan_get_msg_cmd(struct ipmi_smi *intf, 417662306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 417762306a36Sopenharmony_ci{ 417862306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 417962306a36Sopenharmony_ci int rv = 0; 418062306a36Sopenharmony_ci unsigned char netfn; 418162306a36Sopenharmony_ci unsigned char cmd; 418262306a36Sopenharmony_ci unsigned char chan; 418362306a36Sopenharmony_ci struct ipmi_user *user = NULL; 418462306a36Sopenharmony_ci struct ipmi_lan_addr *lan_addr; 418562306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 418662306a36Sopenharmony_ci 418762306a36Sopenharmony_ci if (msg->rsp_size < 12) { 418862306a36Sopenharmony_ci /* Message not big enough, just ignore it. */ 418962306a36Sopenharmony_ci ipmi_inc_stat(intf, invalid_commands); 419062306a36Sopenharmony_ci return 0; 419162306a36Sopenharmony_ci } 419262306a36Sopenharmony_ci 419362306a36Sopenharmony_ci if (msg->rsp[2] != 0) { 419462306a36Sopenharmony_ci /* An error getting the response, just ignore it. */ 419562306a36Sopenharmony_ci return 0; 419662306a36Sopenharmony_ci } 419762306a36Sopenharmony_ci 419862306a36Sopenharmony_ci netfn = msg->rsp[6] >> 2; 419962306a36Sopenharmony_ci cmd = msg->rsp[10]; 420062306a36Sopenharmony_ci chan = msg->rsp[3] & 0xf; 420162306a36Sopenharmony_ci 420262306a36Sopenharmony_ci rcu_read_lock(); 420362306a36Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); 420462306a36Sopenharmony_ci if (rcvr) { 420562306a36Sopenharmony_ci user = rcvr->user; 420662306a36Sopenharmony_ci kref_get(&user->refcount); 420762306a36Sopenharmony_ci } else 420862306a36Sopenharmony_ci user = NULL; 420962306a36Sopenharmony_ci rcu_read_unlock(); 421062306a36Sopenharmony_ci 421162306a36Sopenharmony_ci if (user == NULL) { 421262306a36Sopenharmony_ci /* We didn't find a user, just give up. */ 421362306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 421462306a36Sopenharmony_ci 421562306a36Sopenharmony_ci /* 421662306a36Sopenharmony_ci * Don't do anything with these messages, just allow 421762306a36Sopenharmony_ci * them to be freed. 421862306a36Sopenharmony_ci */ 421962306a36Sopenharmony_ci rv = 0; 422062306a36Sopenharmony_ci } else { 422162306a36Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 422262306a36Sopenharmony_ci if (!recv_msg) { 422362306a36Sopenharmony_ci /* 422462306a36Sopenharmony_ci * We couldn't allocate memory for the 422562306a36Sopenharmony_ci * message, so requeue it for handling later. 422662306a36Sopenharmony_ci */ 422762306a36Sopenharmony_ci rv = 1; 422862306a36Sopenharmony_ci kref_put(&user->refcount, free_user); 422962306a36Sopenharmony_ci } else { 423062306a36Sopenharmony_ci /* Extract the source address from the data. */ 423162306a36Sopenharmony_ci lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr; 423262306a36Sopenharmony_ci lan_addr->addr_type = IPMI_LAN_ADDR_TYPE; 423362306a36Sopenharmony_ci lan_addr->session_handle = msg->rsp[4]; 423462306a36Sopenharmony_ci lan_addr->remote_SWID = msg->rsp[8]; 423562306a36Sopenharmony_ci lan_addr->local_SWID = msg->rsp[5]; 423662306a36Sopenharmony_ci lan_addr->lun = msg->rsp[9] & 3; 423762306a36Sopenharmony_ci lan_addr->channel = msg->rsp[3] & 0xf; 423862306a36Sopenharmony_ci lan_addr->privilege = msg->rsp[3] >> 4; 423962306a36Sopenharmony_ci 424062306a36Sopenharmony_ci /* 424162306a36Sopenharmony_ci * Extract the rest of the message information 424262306a36Sopenharmony_ci * from the IPMB header. 424362306a36Sopenharmony_ci */ 424462306a36Sopenharmony_ci recv_msg->user = user; 424562306a36Sopenharmony_ci recv_msg->recv_type = IPMI_CMD_RECV_TYPE; 424662306a36Sopenharmony_ci recv_msg->msgid = msg->rsp[9] >> 2; 424762306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[6] >> 2; 424862306a36Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[10]; 424962306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 425062306a36Sopenharmony_ci 425162306a36Sopenharmony_ci /* 425262306a36Sopenharmony_ci * We chop off 12, not 11 bytes because the checksum 425362306a36Sopenharmony_ci * at the end also needs to be removed. 425462306a36Sopenharmony_ci */ 425562306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 12; 425662306a36Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[11], 425762306a36Sopenharmony_ci msg->rsp_size - 12); 425862306a36Sopenharmony_ci if (deliver_response(intf, recv_msg)) 425962306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 426062306a36Sopenharmony_ci else 426162306a36Sopenharmony_ci ipmi_inc_stat(intf, handled_commands); 426262306a36Sopenharmony_ci } 426362306a36Sopenharmony_ci } 426462306a36Sopenharmony_ci 426562306a36Sopenharmony_ci return rv; 426662306a36Sopenharmony_ci} 426762306a36Sopenharmony_ci 426862306a36Sopenharmony_ci/* 426962306a36Sopenharmony_ci * This routine will handle "Get Message" command responses with 427062306a36Sopenharmony_ci * channels that use an OEM Medium. The message format belongs to 427162306a36Sopenharmony_ci * the OEM. See IPMI 2.0 specification, Chapter 6 and 427262306a36Sopenharmony_ci * Chapter 22, sections 22.6 and 22.24 for more details. 427362306a36Sopenharmony_ci */ 427462306a36Sopenharmony_cistatic int handle_oem_get_msg_cmd(struct ipmi_smi *intf, 427562306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 427662306a36Sopenharmony_ci{ 427762306a36Sopenharmony_ci struct cmd_rcvr *rcvr; 427862306a36Sopenharmony_ci int rv = 0; 427962306a36Sopenharmony_ci unsigned char netfn; 428062306a36Sopenharmony_ci unsigned char cmd; 428162306a36Sopenharmony_ci unsigned char chan; 428262306a36Sopenharmony_ci struct ipmi_user *user = NULL; 428362306a36Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr; 428462306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 428562306a36Sopenharmony_ci 428662306a36Sopenharmony_ci /* 428762306a36Sopenharmony_ci * We expect the OEM SW to perform error checking 428862306a36Sopenharmony_ci * so we just do some basic sanity checks 428962306a36Sopenharmony_ci */ 429062306a36Sopenharmony_ci if (msg->rsp_size < 4) { 429162306a36Sopenharmony_ci /* Message not big enough, just ignore it. */ 429262306a36Sopenharmony_ci ipmi_inc_stat(intf, invalid_commands); 429362306a36Sopenharmony_ci return 0; 429462306a36Sopenharmony_ci } 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ci if (msg->rsp[2] != 0) { 429762306a36Sopenharmony_ci /* An error getting the response, just ignore it. */ 429862306a36Sopenharmony_ci return 0; 429962306a36Sopenharmony_ci } 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci /* 430262306a36Sopenharmony_ci * This is an OEM Message so the OEM needs to know how 430362306a36Sopenharmony_ci * handle the message. We do no interpretation. 430462306a36Sopenharmony_ci */ 430562306a36Sopenharmony_ci netfn = msg->rsp[0] >> 2; 430662306a36Sopenharmony_ci cmd = msg->rsp[1]; 430762306a36Sopenharmony_ci chan = msg->rsp[3] & 0xf; 430862306a36Sopenharmony_ci 430962306a36Sopenharmony_ci rcu_read_lock(); 431062306a36Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); 431162306a36Sopenharmony_ci if (rcvr) { 431262306a36Sopenharmony_ci user = rcvr->user; 431362306a36Sopenharmony_ci kref_get(&user->refcount); 431462306a36Sopenharmony_ci } else 431562306a36Sopenharmony_ci user = NULL; 431662306a36Sopenharmony_ci rcu_read_unlock(); 431762306a36Sopenharmony_ci 431862306a36Sopenharmony_ci if (user == NULL) { 431962306a36Sopenharmony_ci /* We didn't find a user, just give up. */ 432062306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_ci /* 432362306a36Sopenharmony_ci * Don't do anything with these messages, just allow 432462306a36Sopenharmony_ci * them to be freed. 432562306a36Sopenharmony_ci */ 432662306a36Sopenharmony_ci 432762306a36Sopenharmony_ci rv = 0; 432862306a36Sopenharmony_ci } else { 432962306a36Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 433062306a36Sopenharmony_ci if (!recv_msg) { 433162306a36Sopenharmony_ci /* 433262306a36Sopenharmony_ci * We couldn't allocate memory for the 433362306a36Sopenharmony_ci * message, so requeue it for handling 433462306a36Sopenharmony_ci * later. 433562306a36Sopenharmony_ci */ 433662306a36Sopenharmony_ci rv = 1; 433762306a36Sopenharmony_ci kref_put(&user->refcount, free_user); 433862306a36Sopenharmony_ci } else { 433962306a36Sopenharmony_ci /* 434062306a36Sopenharmony_ci * OEM Messages are expected to be delivered via 434162306a36Sopenharmony_ci * the system interface to SMS software. We might 434262306a36Sopenharmony_ci * need to visit this again depending on OEM 434362306a36Sopenharmony_ci * requirements 434462306a36Sopenharmony_ci */ 434562306a36Sopenharmony_ci smi_addr = ((struct ipmi_system_interface_addr *) 434662306a36Sopenharmony_ci &recv_msg->addr); 434762306a36Sopenharmony_ci smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 434862306a36Sopenharmony_ci smi_addr->channel = IPMI_BMC_CHANNEL; 434962306a36Sopenharmony_ci smi_addr->lun = msg->rsp[0] & 3; 435062306a36Sopenharmony_ci 435162306a36Sopenharmony_ci recv_msg->user = user; 435262306a36Sopenharmony_ci recv_msg->user_msg_data = NULL; 435362306a36Sopenharmony_ci recv_msg->recv_type = IPMI_OEM_RECV_TYPE; 435462306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[0] >> 2; 435562306a36Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[1]; 435662306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 435762306a36Sopenharmony_ci 435862306a36Sopenharmony_ci /* 435962306a36Sopenharmony_ci * The message starts at byte 4 which follows the 436062306a36Sopenharmony_ci * Channel Byte in the "GET MESSAGE" command 436162306a36Sopenharmony_ci */ 436262306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 4; 436362306a36Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[4], 436462306a36Sopenharmony_ci msg->rsp_size - 4); 436562306a36Sopenharmony_ci if (deliver_response(intf, recv_msg)) 436662306a36Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 436762306a36Sopenharmony_ci else 436862306a36Sopenharmony_ci ipmi_inc_stat(intf, handled_commands); 436962306a36Sopenharmony_ci } 437062306a36Sopenharmony_ci } 437162306a36Sopenharmony_ci 437262306a36Sopenharmony_ci return rv; 437362306a36Sopenharmony_ci} 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_cistatic void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg, 437662306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 437762306a36Sopenharmony_ci{ 437862306a36Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr; 437962306a36Sopenharmony_ci 438062306a36Sopenharmony_ci recv_msg->msgid = 0; 438162306a36Sopenharmony_ci smi_addr = (struct ipmi_system_interface_addr *) &recv_msg->addr; 438262306a36Sopenharmony_ci smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 438362306a36Sopenharmony_ci smi_addr->channel = IPMI_BMC_CHANNEL; 438462306a36Sopenharmony_ci smi_addr->lun = msg->rsp[0] & 3; 438562306a36Sopenharmony_ci recv_msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE; 438662306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[0] >> 2; 438762306a36Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[1]; 438862306a36Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[3], msg->rsp_size - 3); 438962306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 439062306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 3; 439162306a36Sopenharmony_ci} 439262306a36Sopenharmony_ci 439362306a36Sopenharmony_cistatic int handle_read_event_rsp(struct ipmi_smi *intf, 439462306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 439562306a36Sopenharmony_ci{ 439662306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg, *recv_msg2; 439762306a36Sopenharmony_ci struct list_head msgs; 439862306a36Sopenharmony_ci struct ipmi_user *user; 439962306a36Sopenharmony_ci int rv = 0, deliver_count = 0, index; 440062306a36Sopenharmony_ci unsigned long flags; 440162306a36Sopenharmony_ci 440262306a36Sopenharmony_ci if (msg->rsp_size < 19) { 440362306a36Sopenharmony_ci /* Message is too small to be an IPMB event. */ 440462306a36Sopenharmony_ci ipmi_inc_stat(intf, invalid_events); 440562306a36Sopenharmony_ci return 0; 440662306a36Sopenharmony_ci } 440762306a36Sopenharmony_ci 440862306a36Sopenharmony_ci if (msg->rsp[2] != 0) { 440962306a36Sopenharmony_ci /* An error getting the event, just ignore it. */ 441062306a36Sopenharmony_ci return 0; 441162306a36Sopenharmony_ci } 441262306a36Sopenharmony_ci 441362306a36Sopenharmony_ci INIT_LIST_HEAD(&msgs); 441462306a36Sopenharmony_ci 441562306a36Sopenharmony_ci spin_lock_irqsave(&intf->events_lock, flags); 441662306a36Sopenharmony_ci 441762306a36Sopenharmony_ci ipmi_inc_stat(intf, events); 441862306a36Sopenharmony_ci 441962306a36Sopenharmony_ci /* 442062306a36Sopenharmony_ci * Allocate and fill in one message for every user that is 442162306a36Sopenharmony_ci * getting events. 442262306a36Sopenharmony_ci */ 442362306a36Sopenharmony_ci index = srcu_read_lock(&intf->users_srcu); 442462306a36Sopenharmony_ci list_for_each_entry_rcu(user, &intf->users, link) { 442562306a36Sopenharmony_ci if (!user->gets_events) 442662306a36Sopenharmony_ci continue; 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 442962306a36Sopenharmony_ci if (!recv_msg) { 443062306a36Sopenharmony_ci rcu_read_unlock(); 443162306a36Sopenharmony_ci list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, 443262306a36Sopenharmony_ci link) { 443362306a36Sopenharmony_ci list_del(&recv_msg->link); 443462306a36Sopenharmony_ci ipmi_free_recv_msg(recv_msg); 443562306a36Sopenharmony_ci } 443662306a36Sopenharmony_ci /* 443762306a36Sopenharmony_ci * We couldn't allocate memory for the 443862306a36Sopenharmony_ci * message, so requeue it for handling 443962306a36Sopenharmony_ci * later. 444062306a36Sopenharmony_ci */ 444162306a36Sopenharmony_ci rv = 1; 444262306a36Sopenharmony_ci goto out; 444362306a36Sopenharmony_ci } 444462306a36Sopenharmony_ci 444562306a36Sopenharmony_ci deliver_count++; 444662306a36Sopenharmony_ci 444762306a36Sopenharmony_ci copy_event_into_recv_msg(recv_msg, msg); 444862306a36Sopenharmony_ci recv_msg->user = user; 444962306a36Sopenharmony_ci kref_get(&user->refcount); 445062306a36Sopenharmony_ci list_add_tail(&recv_msg->link, &msgs); 445162306a36Sopenharmony_ci } 445262306a36Sopenharmony_ci srcu_read_unlock(&intf->users_srcu, index); 445362306a36Sopenharmony_ci 445462306a36Sopenharmony_ci if (deliver_count) { 445562306a36Sopenharmony_ci /* Now deliver all the messages. */ 445662306a36Sopenharmony_ci list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { 445762306a36Sopenharmony_ci list_del(&recv_msg->link); 445862306a36Sopenharmony_ci deliver_local_response(intf, recv_msg); 445962306a36Sopenharmony_ci } 446062306a36Sopenharmony_ci } else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) { 446162306a36Sopenharmony_ci /* 446262306a36Sopenharmony_ci * No one to receive the message, put it in queue if there's 446362306a36Sopenharmony_ci * not already too many things in the queue. 446462306a36Sopenharmony_ci */ 446562306a36Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 446662306a36Sopenharmony_ci if (!recv_msg) { 446762306a36Sopenharmony_ci /* 446862306a36Sopenharmony_ci * We couldn't allocate memory for the 446962306a36Sopenharmony_ci * message, so requeue it for handling 447062306a36Sopenharmony_ci * later. 447162306a36Sopenharmony_ci */ 447262306a36Sopenharmony_ci rv = 1; 447362306a36Sopenharmony_ci goto out; 447462306a36Sopenharmony_ci } 447562306a36Sopenharmony_ci 447662306a36Sopenharmony_ci copy_event_into_recv_msg(recv_msg, msg); 447762306a36Sopenharmony_ci list_add_tail(&recv_msg->link, &intf->waiting_events); 447862306a36Sopenharmony_ci intf->waiting_events_count++; 447962306a36Sopenharmony_ci } else if (!intf->event_msg_printed) { 448062306a36Sopenharmony_ci /* 448162306a36Sopenharmony_ci * There's too many things in the queue, discard this 448262306a36Sopenharmony_ci * message. 448362306a36Sopenharmony_ci */ 448462306a36Sopenharmony_ci dev_warn(intf->si_dev, 448562306a36Sopenharmony_ci "Event queue full, discarding incoming events\n"); 448662306a36Sopenharmony_ci intf->event_msg_printed = 1; 448762306a36Sopenharmony_ci } 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_ci out: 449062306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->events_lock, flags); 449162306a36Sopenharmony_ci 449262306a36Sopenharmony_ci return rv; 449362306a36Sopenharmony_ci} 449462306a36Sopenharmony_ci 449562306a36Sopenharmony_cistatic int handle_bmc_rsp(struct ipmi_smi *intf, 449662306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 449762306a36Sopenharmony_ci{ 449862306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 449962306a36Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr; 450062306a36Sopenharmony_ci 450162306a36Sopenharmony_ci recv_msg = msg->user_data; 450262306a36Sopenharmony_ci if (recv_msg == NULL) { 450362306a36Sopenharmony_ci dev_warn(intf->si_dev, 450462306a36Sopenharmony_ci "IPMI SMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n"); 450562306a36Sopenharmony_ci return 0; 450662306a36Sopenharmony_ci } 450762306a36Sopenharmony_ci 450862306a36Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 450962306a36Sopenharmony_ci recv_msg->msgid = msg->msgid; 451062306a36Sopenharmony_ci smi_addr = ((struct ipmi_system_interface_addr *) 451162306a36Sopenharmony_ci &recv_msg->addr); 451262306a36Sopenharmony_ci smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 451362306a36Sopenharmony_ci smi_addr->channel = IPMI_BMC_CHANNEL; 451462306a36Sopenharmony_ci smi_addr->lun = msg->rsp[0] & 3; 451562306a36Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[0] >> 2; 451662306a36Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[1]; 451762306a36Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[2], msg->rsp_size - 2); 451862306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 451962306a36Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 2; 452062306a36Sopenharmony_ci deliver_local_response(intf, recv_msg); 452162306a36Sopenharmony_ci 452262306a36Sopenharmony_ci return 0; 452362306a36Sopenharmony_ci} 452462306a36Sopenharmony_ci 452562306a36Sopenharmony_ci/* 452662306a36Sopenharmony_ci * Handle a received message. Return 1 if the message should be requeued, 452762306a36Sopenharmony_ci * 0 if the message should be freed, or -1 if the message should not 452862306a36Sopenharmony_ci * be freed or requeued. 452962306a36Sopenharmony_ci */ 453062306a36Sopenharmony_cistatic int handle_one_recv_msg(struct ipmi_smi *intf, 453162306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 453262306a36Sopenharmony_ci{ 453362306a36Sopenharmony_ci int requeue = 0; 453462306a36Sopenharmony_ci int chan; 453562306a36Sopenharmony_ci unsigned char cc; 453662306a36Sopenharmony_ci bool is_cmd = !((msg->rsp[0] >> 2) & 1); 453762306a36Sopenharmony_ci 453862306a36Sopenharmony_ci dev_dbg(intf->si_dev, "Recv: %*ph\n", msg->rsp_size, msg->rsp); 453962306a36Sopenharmony_ci 454062306a36Sopenharmony_ci if (msg->rsp_size < 2) { 454162306a36Sopenharmony_ci /* Message is too small to be correct. */ 454262306a36Sopenharmony_ci dev_warn(intf->si_dev, 454362306a36Sopenharmony_ci "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n", 454462306a36Sopenharmony_ci (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size); 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_cireturn_unspecified: 454762306a36Sopenharmony_ci /* Generate an error response for the message. */ 454862306a36Sopenharmony_ci msg->rsp[0] = msg->data[0] | (1 << 2); 454962306a36Sopenharmony_ci msg->rsp[1] = msg->data[1]; 455062306a36Sopenharmony_ci msg->rsp[2] = IPMI_ERR_UNSPECIFIED; 455162306a36Sopenharmony_ci msg->rsp_size = 3; 455262306a36Sopenharmony_ci } else if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) { 455362306a36Sopenharmony_ci /* commands must have at least 4 bytes, responses 5. */ 455462306a36Sopenharmony_ci if (is_cmd && (msg->rsp_size < 4)) { 455562306a36Sopenharmony_ci ipmi_inc_stat(intf, invalid_commands); 455662306a36Sopenharmony_ci goto out; 455762306a36Sopenharmony_ci } 455862306a36Sopenharmony_ci if (!is_cmd && (msg->rsp_size < 5)) { 455962306a36Sopenharmony_ci ipmi_inc_stat(intf, invalid_ipmb_responses); 456062306a36Sopenharmony_ci /* Construct a valid error response. */ 456162306a36Sopenharmony_ci msg->rsp[0] = msg->data[0] & 0xfc; /* NetFN */ 456262306a36Sopenharmony_ci msg->rsp[0] |= (1 << 2); /* Make it a response */ 456362306a36Sopenharmony_ci msg->rsp[0] |= msg->data[2] & 3; /* rqLUN */ 456462306a36Sopenharmony_ci msg->rsp[1] = msg->data[1]; /* Addr */ 456562306a36Sopenharmony_ci msg->rsp[2] = msg->data[2] & 0xfc; /* rqSeq */ 456662306a36Sopenharmony_ci msg->rsp[2] |= msg->data[0] & 0x3; /* rsLUN */ 456762306a36Sopenharmony_ci msg->rsp[3] = msg->data[3]; /* Cmd */ 456862306a36Sopenharmony_ci msg->rsp[4] = IPMI_ERR_UNSPECIFIED; 456962306a36Sopenharmony_ci msg->rsp_size = 5; 457062306a36Sopenharmony_ci } 457162306a36Sopenharmony_ci } else if ((msg->data_size >= 2) 457262306a36Sopenharmony_ci && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) 457362306a36Sopenharmony_ci && (msg->data[1] == IPMI_SEND_MSG_CMD) 457462306a36Sopenharmony_ci && (msg->user_data == NULL)) { 457562306a36Sopenharmony_ci 457662306a36Sopenharmony_ci if (intf->in_shutdown) 457762306a36Sopenharmony_ci goto out; 457862306a36Sopenharmony_ci 457962306a36Sopenharmony_ci /* 458062306a36Sopenharmony_ci * This is the local response to a command send, start 458162306a36Sopenharmony_ci * the timer for these. The user_data will not be 458262306a36Sopenharmony_ci * NULL if this is a response send, and we will let 458362306a36Sopenharmony_ci * response sends just go through. 458462306a36Sopenharmony_ci */ 458562306a36Sopenharmony_ci 458662306a36Sopenharmony_ci /* 458762306a36Sopenharmony_ci * Check for errors, if we get certain errors (ones 458862306a36Sopenharmony_ci * that mean basically we can try again later), we 458962306a36Sopenharmony_ci * ignore them and start the timer. Otherwise we 459062306a36Sopenharmony_ci * report the error immediately. 459162306a36Sopenharmony_ci */ 459262306a36Sopenharmony_ci if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0) 459362306a36Sopenharmony_ci && (msg->rsp[2] != IPMI_NODE_BUSY_ERR) 459462306a36Sopenharmony_ci && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR) 459562306a36Sopenharmony_ci && (msg->rsp[2] != IPMI_BUS_ERR) 459662306a36Sopenharmony_ci && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) { 459762306a36Sopenharmony_ci int ch = msg->rsp[3] & 0xf; 459862306a36Sopenharmony_ci struct ipmi_channel *chans; 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_ci /* Got an error sending the message, handle it. */ 460162306a36Sopenharmony_ci 460262306a36Sopenharmony_ci chans = READ_ONCE(intf->channel_list)->c; 460362306a36Sopenharmony_ci if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN) 460462306a36Sopenharmony_ci || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC)) 460562306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_lan_command_errs); 460662306a36Sopenharmony_ci else 460762306a36Sopenharmony_ci ipmi_inc_stat(intf, sent_ipmb_command_errs); 460862306a36Sopenharmony_ci intf_err_seq(intf, msg->msgid, msg->rsp[2]); 460962306a36Sopenharmony_ci } else 461062306a36Sopenharmony_ci /* The message was sent, start the timer. */ 461162306a36Sopenharmony_ci intf_start_seq_timer(intf, msg->msgid); 461262306a36Sopenharmony_ci requeue = 0; 461362306a36Sopenharmony_ci goto out; 461462306a36Sopenharmony_ci } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1)) 461562306a36Sopenharmony_ci || (msg->rsp[1] != msg->data[1])) { 461662306a36Sopenharmony_ci /* 461762306a36Sopenharmony_ci * The NetFN and Command in the response is not even 461862306a36Sopenharmony_ci * marginally correct. 461962306a36Sopenharmony_ci */ 462062306a36Sopenharmony_ci dev_warn(intf->si_dev, 462162306a36Sopenharmony_ci "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n", 462262306a36Sopenharmony_ci (msg->data[0] >> 2) | 1, msg->data[1], 462362306a36Sopenharmony_ci msg->rsp[0] >> 2, msg->rsp[1]); 462462306a36Sopenharmony_ci 462562306a36Sopenharmony_ci goto return_unspecified; 462662306a36Sopenharmony_ci } 462762306a36Sopenharmony_ci 462862306a36Sopenharmony_ci if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) { 462962306a36Sopenharmony_ci if ((msg->data[0] >> 2) & 1) { 463062306a36Sopenharmony_ci /* It's a response to a sent response. */ 463162306a36Sopenharmony_ci chan = 0; 463262306a36Sopenharmony_ci cc = msg->rsp[4]; 463362306a36Sopenharmony_ci goto process_response_response; 463462306a36Sopenharmony_ci } 463562306a36Sopenharmony_ci if (is_cmd) 463662306a36Sopenharmony_ci requeue = handle_ipmb_direct_rcv_cmd(intf, msg); 463762306a36Sopenharmony_ci else 463862306a36Sopenharmony_ci requeue = handle_ipmb_direct_rcv_rsp(intf, msg); 463962306a36Sopenharmony_ci } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) 464062306a36Sopenharmony_ci && (msg->rsp[1] == IPMI_SEND_MSG_CMD) 464162306a36Sopenharmony_ci && (msg->user_data != NULL)) { 464262306a36Sopenharmony_ci /* 464362306a36Sopenharmony_ci * It's a response to a response we sent. For this we 464462306a36Sopenharmony_ci * deliver a send message response to the user. 464562306a36Sopenharmony_ci */ 464662306a36Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 464762306a36Sopenharmony_ci 464862306a36Sopenharmony_ci chan = msg->data[2] & 0x0f; 464962306a36Sopenharmony_ci if (chan >= IPMI_MAX_CHANNELS) 465062306a36Sopenharmony_ci /* Invalid channel number */ 465162306a36Sopenharmony_ci goto out; 465262306a36Sopenharmony_ci cc = msg->rsp[2]; 465362306a36Sopenharmony_ci 465462306a36Sopenharmony_ciprocess_response_response: 465562306a36Sopenharmony_ci recv_msg = msg->user_data; 465662306a36Sopenharmony_ci 465762306a36Sopenharmony_ci requeue = 0; 465862306a36Sopenharmony_ci if (!recv_msg) 465962306a36Sopenharmony_ci goto out; 466062306a36Sopenharmony_ci 466162306a36Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE; 466262306a36Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 466362306a36Sopenharmony_ci recv_msg->msg_data[0] = cc; 466462306a36Sopenharmony_ci recv_msg->msg.data_len = 1; 466562306a36Sopenharmony_ci deliver_local_response(intf, recv_msg); 466662306a36Sopenharmony_ci } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) 466762306a36Sopenharmony_ci && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { 466862306a36Sopenharmony_ci struct ipmi_channel *chans; 466962306a36Sopenharmony_ci 467062306a36Sopenharmony_ci /* It's from the receive queue. */ 467162306a36Sopenharmony_ci chan = msg->rsp[3] & 0xf; 467262306a36Sopenharmony_ci if (chan >= IPMI_MAX_CHANNELS) { 467362306a36Sopenharmony_ci /* Invalid channel number */ 467462306a36Sopenharmony_ci requeue = 0; 467562306a36Sopenharmony_ci goto out; 467662306a36Sopenharmony_ci } 467762306a36Sopenharmony_ci 467862306a36Sopenharmony_ci /* 467962306a36Sopenharmony_ci * We need to make sure the channels have been initialized. 468062306a36Sopenharmony_ci * The channel_handler routine will set the "curr_channel" 468162306a36Sopenharmony_ci * equal to or greater than IPMI_MAX_CHANNELS when all the 468262306a36Sopenharmony_ci * channels for this interface have been initialized. 468362306a36Sopenharmony_ci */ 468462306a36Sopenharmony_ci if (!intf->channels_ready) { 468562306a36Sopenharmony_ci requeue = 0; /* Throw the message away */ 468662306a36Sopenharmony_ci goto out; 468762306a36Sopenharmony_ci } 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci chans = READ_ONCE(intf->channel_list)->c; 469062306a36Sopenharmony_ci 469162306a36Sopenharmony_ci switch (chans[chan].medium) { 469262306a36Sopenharmony_ci case IPMI_CHANNEL_MEDIUM_IPMB: 469362306a36Sopenharmony_ci if (msg->rsp[4] & 0x04) { 469462306a36Sopenharmony_ci /* 469562306a36Sopenharmony_ci * It's a response, so find the 469662306a36Sopenharmony_ci * requesting message and send it up. 469762306a36Sopenharmony_ci */ 469862306a36Sopenharmony_ci requeue = handle_ipmb_get_msg_rsp(intf, msg); 469962306a36Sopenharmony_ci } else { 470062306a36Sopenharmony_ci /* 470162306a36Sopenharmony_ci * It's a command to the SMS from some other 470262306a36Sopenharmony_ci * entity. Handle that. 470362306a36Sopenharmony_ci */ 470462306a36Sopenharmony_ci requeue = handle_ipmb_get_msg_cmd(intf, msg); 470562306a36Sopenharmony_ci } 470662306a36Sopenharmony_ci break; 470762306a36Sopenharmony_ci 470862306a36Sopenharmony_ci case IPMI_CHANNEL_MEDIUM_8023LAN: 470962306a36Sopenharmony_ci case IPMI_CHANNEL_MEDIUM_ASYNC: 471062306a36Sopenharmony_ci if (msg->rsp[6] & 0x04) { 471162306a36Sopenharmony_ci /* 471262306a36Sopenharmony_ci * It's a response, so find the 471362306a36Sopenharmony_ci * requesting message and send it up. 471462306a36Sopenharmony_ci */ 471562306a36Sopenharmony_ci requeue = handle_lan_get_msg_rsp(intf, msg); 471662306a36Sopenharmony_ci } else { 471762306a36Sopenharmony_ci /* 471862306a36Sopenharmony_ci * It's a command to the SMS from some other 471962306a36Sopenharmony_ci * entity. Handle that. 472062306a36Sopenharmony_ci */ 472162306a36Sopenharmony_ci requeue = handle_lan_get_msg_cmd(intf, msg); 472262306a36Sopenharmony_ci } 472362306a36Sopenharmony_ci break; 472462306a36Sopenharmony_ci 472562306a36Sopenharmony_ci default: 472662306a36Sopenharmony_ci /* Check for OEM Channels. Clients had better 472762306a36Sopenharmony_ci register for these commands. */ 472862306a36Sopenharmony_ci if ((chans[chan].medium >= IPMI_CHANNEL_MEDIUM_OEM_MIN) 472962306a36Sopenharmony_ci && (chans[chan].medium 473062306a36Sopenharmony_ci <= IPMI_CHANNEL_MEDIUM_OEM_MAX)) { 473162306a36Sopenharmony_ci requeue = handle_oem_get_msg_cmd(intf, msg); 473262306a36Sopenharmony_ci } else { 473362306a36Sopenharmony_ci /* 473462306a36Sopenharmony_ci * We don't handle the channel type, so just 473562306a36Sopenharmony_ci * free the message. 473662306a36Sopenharmony_ci */ 473762306a36Sopenharmony_ci requeue = 0; 473862306a36Sopenharmony_ci } 473962306a36Sopenharmony_ci } 474062306a36Sopenharmony_ci 474162306a36Sopenharmony_ci } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) 474262306a36Sopenharmony_ci && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { 474362306a36Sopenharmony_ci /* It's an asynchronous event. */ 474462306a36Sopenharmony_ci requeue = handle_read_event_rsp(intf, msg); 474562306a36Sopenharmony_ci } else { 474662306a36Sopenharmony_ci /* It's a response from the local BMC. */ 474762306a36Sopenharmony_ci requeue = handle_bmc_rsp(intf, msg); 474862306a36Sopenharmony_ci } 474962306a36Sopenharmony_ci 475062306a36Sopenharmony_ci out: 475162306a36Sopenharmony_ci return requeue; 475262306a36Sopenharmony_ci} 475362306a36Sopenharmony_ci 475462306a36Sopenharmony_ci/* 475562306a36Sopenharmony_ci * If there are messages in the queue or pretimeouts, handle them. 475662306a36Sopenharmony_ci */ 475762306a36Sopenharmony_cistatic void handle_new_recv_msgs(struct ipmi_smi *intf) 475862306a36Sopenharmony_ci{ 475962306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg; 476062306a36Sopenharmony_ci unsigned long flags = 0; 476162306a36Sopenharmony_ci int rv; 476262306a36Sopenharmony_ci int run_to_completion = intf->run_to_completion; 476362306a36Sopenharmony_ci 476462306a36Sopenharmony_ci /* See if any waiting messages need to be processed. */ 476562306a36Sopenharmony_ci if (!run_to_completion) 476662306a36Sopenharmony_ci spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); 476762306a36Sopenharmony_ci while (!list_empty(&intf->waiting_rcv_msgs)) { 476862306a36Sopenharmony_ci smi_msg = list_entry(intf->waiting_rcv_msgs.next, 476962306a36Sopenharmony_ci struct ipmi_smi_msg, link); 477062306a36Sopenharmony_ci list_del(&smi_msg->link); 477162306a36Sopenharmony_ci if (!run_to_completion) 477262306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, 477362306a36Sopenharmony_ci flags); 477462306a36Sopenharmony_ci rv = handle_one_recv_msg(intf, smi_msg); 477562306a36Sopenharmony_ci if (!run_to_completion) 477662306a36Sopenharmony_ci spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); 477762306a36Sopenharmony_ci if (rv > 0) { 477862306a36Sopenharmony_ci /* 477962306a36Sopenharmony_ci * To preserve message order, quit if we 478062306a36Sopenharmony_ci * can't handle a message. Add the message 478162306a36Sopenharmony_ci * back at the head, this is safe because this 478262306a36Sopenharmony_ci * tasklet is the only thing that pulls the 478362306a36Sopenharmony_ci * messages. 478462306a36Sopenharmony_ci */ 478562306a36Sopenharmony_ci list_add(&smi_msg->link, &intf->waiting_rcv_msgs); 478662306a36Sopenharmony_ci break; 478762306a36Sopenharmony_ci } else { 478862306a36Sopenharmony_ci if (rv == 0) 478962306a36Sopenharmony_ci /* Message handled */ 479062306a36Sopenharmony_ci ipmi_free_smi_msg(smi_msg); 479162306a36Sopenharmony_ci /* If rv < 0, fatal error, del but don't free. */ 479262306a36Sopenharmony_ci } 479362306a36Sopenharmony_ci } 479462306a36Sopenharmony_ci if (!run_to_completion) 479562306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, flags); 479662306a36Sopenharmony_ci 479762306a36Sopenharmony_ci /* 479862306a36Sopenharmony_ci * If the pretimout count is non-zero, decrement one from it and 479962306a36Sopenharmony_ci * deliver pretimeouts to all the users. 480062306a36Sopenharmony_ci */ 480162306a36Sopenharmony_ci if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { 480262306a36Sopenharmony_ci struct ipmi_user *user; 480362306a36Sopenharmony_ci int index; 480462306a36Sopenharmony_ci 480562306a36Sopenharmony_ci index = srcu_read_lock(&intf->users_srcu); 480662306a36Sopenharmony_ci list_for_each_entry_rcu(user, &intf->users, link) { 480762306a36Sopenharmony_ci if (user->handler->ipmi_watchdog_pretimeout) 480862306a36Sopenharmony_ci user->handler->ipmi_watchdog_pretimeout( 480962306a36Sopenharmony_ci user->handler_data); 481062306a36Sopenharmony_ci } 481162306a36Sopenharmony_ci srcu_read_unlock(&intf->users_srcu, index); 481262306a36Sopenharmony_ci } 481362306a36Sopenharmony_ci} 481462306a36Sopenharmony_ci 481562306a36Sopenharmony_cistatic void smi_recv_tasklet(struct tasklet_struct *t) 481662306a36Sopenharmony_ci{ 481762306a36Sopenharmony_ci unsigned long flags = 0; /* keep us warning-free. */ 481862306a36Sopenharmony_ci struct ipmi_smi *intf = from_tasklet(intf, t, recv_tasklet); 481962306a36Sopenharmony_ci int run_to_completion = intf->run_to_completion; 482062306a36Sopenharmony_ci struct ipmi_smi_msg *newmsg = NULL; 482162306a36Sopenharmony_ci 482262306a36Sopenharmony_ci /* 482362306a36Sopenharmony_ci * Start the next message if available. 482462306a36Sopenharmony_ci * 482562306a36Sopenharmony_ci * Do this here, not in the actual receiver, because we may deadlock 482662306a36Sopenharmony_ci * because the lower layer is allowed to hold locks while calling 482762306a36Sopenharmony_ci * message delivery. 482862306a36Sopenharmony_ci */ 482962306a36Sopenharmony_ci 483062306a36Sopenharmony_ci rcu_read_lock(); 483162306a36Sopenharmony_ci 483262306a36Sopenharmony_ci if (!run_to_completion) 483362306a36Sopenharmony_ci spin_lock_irqsave(&intf->xmit_msgs_lock, flags); 483462306a36Sopenharmony_ci if (intf->curr_msg == NULL && !intf->in_shutdown) { 483562306a36Sopenharmony_ci struct list_head *entry = NULL; 483662306a36Sopenharmony_ci 483762306a36Sopenharmony_ci /* Pick the high priority queue first. */ 483862306a36Sopenharmony_ci if (!list_empty(&intf->hp_xmit_msgs)) 483962306a36Sopenharmony_ci entry = intf->hp_xmit_msgs.next; 484062306a36Sopenharmony_ci else if (!list_empty(&intf->xmit_msgs)) 484162306a36Sopenharmony_ci entry = intf->xmit_msgs.next; 484262306a36Sopenharmony_ci 484362306a36Sopenharmony_ci if (entry) { 484462306a36Sopenharmony_ci list_del(entry); 484562306a36Sopenharmony_ci newmsg = list_entry(entry, struct ipmi_smi_msg, link); 484662306a36Sopenharmony_ci intf->curr_msg = newmsg; 484762306a36Sopenharmony_ci } 484862306a36Sopenharmony_ci } 484962306a36Sopenharmony_ci 485062306a36Sopenharmony_ci if (!run_to_completion) 485162306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); 485262306a36Sopenharmony_ci if (newmsg) 485362306a36Sopenharmony_ci intf->handlers->sender(intf->send_info, newmsg); 485462306a36Sopenharmony_ci 485562306a36Sopenharmony_ci rcu_read_unlock(); 485662306a36Sopenharmony_ci 485762306a36Sopenharmony_ci handle_new_recv_msgs(intf); 485862306a36Sopenharmony_ci} 485962306a36Sopenharmony_ci 486062306a36Sopenharmony_ci/* Handle a new message from the lower layer. */ 486162306a36Sopenharmony_civoid ipmi_smi_msg_received(struct ipmi_smi *intf, 486262306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 486362306a36Sopenharmony_ci{ 486462306a36Sopenharmony_ci unsigned long flags = 0; /* keep us warning-free. */ 486562306a36Sopenharmony_ci int run_to_completion = intf->run_to_completion; 486662306a36Sopenharmony_ci 486762306a36Sopenharmony_ci /* 486862306a36Sopenharmony_ci * To preserve message order, we keep a queue and deliver from 486962306a36Sopenharmony_ci * a tasklet. 487062306a36Sopenharmony_ci */ 487162306a36Sopenharmony_ci if (!run_to_completion) 487262306a36Sopenharmony_ci spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); 487362306a36Sopenharmony_ci list_add_tail(&msg->link, &intf->waiting_rcv_msgs); 487462306a36Sopenharmony_ci if (!run_to_completion) 487562306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, 487662306a36Sopenharmony_ci flags); 487762306a36Sopenharmony_ci 487862306a36Sopenharmony_ci if (!run_to_completion) 487962306a36Sopenharmony_ci spin_lock_irqsave(&intf->xmit_msgs_lock, flags); 488062306a36Sopenharmony_ci /* 488162306a36Sopenharmony_ci * We can get an asynchronous event or receive message in addition 488262306a36Sopenharmony_ci * to commands we send. 488362306a36Sopenharmony_ci */ 488462306a36Sopenharmony_ci if (msg == intf->curr_msg) 488562306a36Sopenharmony_ci intf->curr_msg = NULL; 488662306a36Sopenharmony_ci if (!run_to_completion) 488762306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); 488862306a36Sopenharmony_ci 488962306a36Sopenharmony_ci if (run_to_completion) 489062306a36Sopenharmony_ci smi_recv_tasklet(&intf->recv_tasklet); 489162306a36Sopenharmony_ci else 489262306a36Sopenharmony_ci tasklet_schedule(&intf->recv_tasklet); 489362306a36Sopenharmony_ci} 489462306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_msg_received); 489562306a36Sopenharmony_ci 489662306a36Sopenharmony_civoid ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf) 489762306a36Sopenharmony_ci{ 489862306a36Sopenharmony_ci if (intf->in_shutdown) 489962306a36Sopenharmony_ci return; 490062306a36Sopenharmony_ci 490162306a36Sopenharmony_ci atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); 490262306a36Sopenharmony_ci tasklet_schedule(&intf->recv_tasklet); 490362306a36Sopenharmony_ci} 490462306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); 490562306a36Sopenharmony_ci 490662306a36Sopenharmony_cistatic struct ipmi_smi_msg * 490762306a36Sopenharmony_cismi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg, 490862306a36Sopenharmony_ci unsigned char seq, long seqid) 490962306a36Sopenharmony_ci{ 491062306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg = ipmi_alloc_smi_msg(); 491162306a36Sopenharmony_ci if (!smi_msg) 491262306a36Sopenharmony_ci /* 491362306a36Sopenharmony_ci * If we can't allocate the message, then just return, we 491462306a36Sopenharmony_ci * get 4 retries, so this should be ok. 491562306a36Sopenharmony_ci */ 491662306a36Sopenharmony_ci return NULL; 491762306a36Sopenharmony_ci 491862306a36Sopenharmony_ci memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len); 491962306a36Sopenharmony_ci smi_msg->data_size = recv_msg->msg.data_len; 492062306a36Sopenharmony_ci smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid); 492162306a36Sopenharmony_ci 492262306a36Sopenharmony_ci dev_dbg(intf->si_dev, "Resend: %*ph\n", 492362306a36Sopenharmony_ci smi_msg->data_size, smi_msg->data); 492462306a36Sopenharmony_ci 492562306a36Sopenharmony_ci return smi_msg; 492662306a36Sopenharmony_ci} 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_cistatic void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, 492962306a36Sopenharmony_ci struct list_head *timeouts, 493062306a36Sopenharmony_ci unsigned long timeout_period, 493162306a36Sopenharmony_ci int slot, unsigned long *flags, 493262306a36Sopenharmony_ci bool *need_timer) 493362306a36Sopenharmony_ci{ 493462306a36Sopenharmony_ci struct ipmi_recv_msg *msg; 493562306a36Sopenharmony_ci 493662306a36Sopenharmony_ci if (intf->in_shutdown) 493762306a36Sopenharmony_ci return; 493862306a36Sopenharmony_ci 493962306a36Sopenharmony_ci if (!ent->inuse) 494062306a36Sopenharmony_ci return; 494162306a36Sopenharmony_ci 494262306a36Sopenharmony_ci if (timeout_period < ent->timeout) { 494362306a36Sopenharmony_ci ent->timeout -= timeout_period; 494462306a36Sopenharmony_ci *need_timer = true; 494562306a36Sopenharmony_ci return; 494662306a36Sopenharmony_ci } 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_ci if (ent->retries_left == 0) { 494962306a36Sopenharmony_ci /* The message has used all its retries. */ 495062306a36Sopenharmony_ci ent->inuse = 0; 495162306a36Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 495262306a36Sopenharmony_ci msg = ent->recv_msg; 495362306a36Sopenharmony_ci list_add_tail(&msg->link, timeouts); 495462306a36Sopenharmony_ci if (ent->broadcast) 495562306a36Sopenharmony_ci ipmi_inc_stat(intf, timed_out_ipmb_broadcasts); 495662306a36Sopenharmony_ci else if (is_lan_addr(&ent->recv_msg->addr)) 495762306a36Sopenharmony_ci ipmi_inc_stat(intf, timed_out_lan_commands); 495862306a36Sopenharmony_ci else 495962306a36Sopenharmony_ci ipmi_inc_stat(intf, timed_out_ipmb_commands); 496062306a36Sopenharmony_ci } else { 496162306a36Sopenharmony_ci struct ipmi_smi_msg *smi_msg; 496262306a36Sopenharmony_ci /* More retries, send again. */ 496362306a36Sopenharmony_ci 496462306a36Sopenharmony_ci *need_timer = true; 496562306a36Sopenharmony_ci 496662306a36Sopenharmony_ci /* 496762306a36Sopenharmony_ci * Start with the max timer, set to normal timer after 496862306a36Sopenharmony_ci * the message is sent. 496962306a36Sopenharmony_ci */ 497062306a36Sopenharmony_ci ent->timeout = MAX_MSG_TIMEOUT; 497162306a36Sopenharmony_ci ent->retries_left--; 497262306a36Sopenharmony_ci smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot, 497362306a36Sopenharmony_ci ent->seqid); 497462306a36Sopenharmony_ci if (!smi_msg) { 497562306a36Sopenharmony_ci if (is_lan_addr(&ent->recv_msg->addr)) 497662306a36Sopenharmony_ci ipmi_inc_stat(intf, 497762306a36Sopenharmony_ci dropped_rexmit_lan_commands); 497862306a36Sopenharmony_ci else 497962306a36Sopenharmony_ci ipmi_inc_stat(intf, 498062306a36Sopenharmony_ci dropped_rexmit_ipmb_commands); 498162306a36Sopenharmony_ci return; 498262306a36Sopenharmony_ci } 498362306a36Sopenharmony_ci 498462306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, *flags); 498562306a36Sopenharmony_ci 498662306a36Sopenharmony_ci /* 498762306a36Sopenharmony_ci * Send the new message. We send with a zero 498862306a36Sopenharmony_ci * priority. It timed out, I doubt time is that 498962306a36Sopenharmony_ci * critical now, and high priority messages are really 499062306a36Sopenharmony_ci * only for messages to the local MC, which don't get 499162306a36Sopenharmony_ci * resent. 499262306a36Sopenharmony_ci */ 499362306a36Sopenharmony_ci if (intf->handlers) { 499462306a36Sopenharmony_ci if (is_lan_addr(&ent->recv_msg->addr)) 499562306a36Sopenharmony_ci ipmi_inc_stat(intf, 499662306a36Sopenharmony_ci retransmitted_lan_commands); 499762306a36Sopenharmony_ci else 499862306a36Sopenharmony_ci ipmi_inc_stat(intf, 499962306a36Sopenharmony_ci retransmitted_ipmb_commands); 500062306a36Sopenharmony_ci 500162306a36Sopenharmony_ci smi_send(intf, intf->handlers, smi_msg, 0); 500262306a36Sopenharmony_ci } else 500362306a36Sopenharmony_ci ipmi_free_smi_msg(smi_msg); 500462306a36Sopenharmony_ci 500562306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, *flags); 500662306a36Sopenharmony_ci } 500762306a36Sopenharmony_ci} 500862306a36Sopenharmony_ci 500962306a36Sopenharmony_cistatic bool ipmi_timeout_handler(struct ipmi_smi *intf, 501062306a36Sopenharmony_ci unsigned long timeout_period) 501162306a36Sopenharmony_ci{ 501262306a36Sopenharmony_ci struct list_head timeouts; 501362306a36Sopenharmony_ci struct ipmi_recv_msg *msg, *msg2; 501462306a36Sopenharmony_ci unsigned long flags; 501562306a36Sopenharmony_ci int i; 501662306a36Sopenharmony_ci bool need_timer = false; 501762306a36Sopenharmony_ci 501862306a36Sopenharmony_ci if (!intf->bmc_registered) { 501962306a36Sopenharmony_ci kref_get(&intf->refcount); 502062306a36Sopenharmony_ci if (!schedule_work(&intf->bmc_reg_work)) { 502162306a36Sopenharmony_ci kref_put(&intf->refcount, intf_free); 502262306a36Sopenharmony_ci need_timer = true; 502362306a36Sopenharmony_ci } 502462306a36Sopenharmony_ci } 502562306a36Sopenharmony_ci 502662306a36Sopenharmony_ci /* 502762306a36Sopenharmony_ci * Go through the seq table and find any messages that 502862306a36Sopenharmony_ci * have timed out, putting them in the timeouts 502962306a36Sopenharmony_ci * list. 503062306a36Sopenharmony_ci */ 503162306a36Sopenharmony_ci INIT_LIST_HEAD(&timeouts); 503262306a36Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 503362306a36Sopenharmony_ci if (intf->ipmb_maintenance_mode_timeout) { 503462306a36Sopenharmony_ci if (intf->ipmb_maintenance_mode_timeout <= timeout_period) 503562306a36Sopenharmony_ci intf->ipmb_maintenance_mode_timeout = 0; 503662306a36Sopenharmony_ci else 503762306a36Sopenharmony_ci intf->ipmb_maintenance_mode_timeout -= timeout_period; 503862306a36Sopenharmony_ci } 503962306a36Sopenharmony_ci for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) 504062306a36Sopenharmony_ci check_msg_timeout(intf, &intf->seq_table[i], 504162306a36Sopenharmony_ci &timeouts, timeout_period, i, 504262306a36Sopenharmony_ci &flags, &need_timer); 504362306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 504462306a36Sopenharmony_ci 504562306a36Sopenharmony_ci list_for_each_entry_safe(msg, msg2, &timeouts, link) 504662306a36Sopenharmony_ci deliver_err_response(intf, msg, IPMI_TIMEOUT_COMPLETION_CODE); 504762306a36Sopenharmony_ci 504862306a36Sopenharmony_ci /* 504962306a36Sopenharmony_ci * Maintenance mode handling. Check the timeout 505062306a36Sopenharmony_ci * optimistically before we claim the lock. It may 505162306a36Sopenharmony_ci * mean a timeout gets missed occasionally, but that 505262306a36Sopenharmony_ci * only means the timeout gets extended by one period 505362306a36Sopenharmony_ci * in that case. No big deal, and it avoids the lock 505462306a36Sopenharmony_ci * most of the time. 505562306a36Sopenharmony_ci */ 505662306a36Sopenharmony_ci if (intf->auto_maintenance_timeout > 0) { 505762306a36Sopenharmony_ci spin_lock_irqsave(&intf->maintenance_mode_lock, flags); 505862306a36Sopenharmony_ci if (intf->auto_maintenance_timeout > 0) { 505962306a36Sopenharmony_ci intf->auto_maintenance_timeout 506062306a36Sopenharmony_ci -= timeout_period; 506162306a36Sopenharmony_ci if (!intf->maintenance_mode 506262306a36Sopenharmony_ci && (intf->auto_maintenance_timeout <= 0)) { 506362306a36Sopenharmony_ci intf->maintenance_mode_enable = false; 506462306a36Sopenharmony_ci maintenance_mode_update(intf); 506562306a36Sopenharmony_ci } 506662306a36Sopenharmony_ci } 506762306a36Sopenharmony_ci spin_unlock_irqrestore(&intf->maintenance_mode_lock, 506862306a36Sopenharmony_ci flags); 506962306a36Sopenharmony_ci } 507062306a36Sopenharmony_ci 507162306a36Sopenharmony_ci tasklet_schedule(&intf->recv_tasklet); 507262306a36Sopenharmony_ci 507362306a36Sopenharmony_ci return need_timer; 507462306a36Sopenharmony_ci} 507562306a36Sopenharmony_ci 507662306a36Sopenharmony_cistatic void ipmi_request_event(struct ipmi_smi *intf) 507762306a36Sopenharmony_ci{ 507862306a36Sopenharmony_ci /* No event requests when in maintenance mode. */ 507962306a36Sopenharmony_ci if (intf->maintenance_mode_enable) 508062306a36Sopenharmony_ci return; 508162306a36Sopenharmony_ci 508262306a36Sopenharmony_ci if (!intf->in_shutdown) 508362306a36Sopenharmony_ci intf->handlers->request_events(intf->send_info); 508462306a36Sopenharmony_ci} 508562306a36Sopenharmony_ci 508662306a36Sopenharmony_cistatic struct timer_list ipmi_timer; 508762306a36Sopenharmony_ci 508862306a36Sopenharmony_cistatic atomic_t stop_operation; 508962306a36Sopenharmony_ci 509062306a36Sopenharmony_cistatic void ipmi_timeout(struct timer_list *unused) 509162306a36Sopenharmony_ci{ 509262306a36Sopenharmony_ci struct ipmi_smi *intf; 509362306a36Sopenharmony_ci bool need_timer = false; 509462306a36Sopenharmony_ci int index; 509562306a36Sopenharmony_ci 509662306a36Sopenharmony_ci if (atomic_read(&stop_operation)) 509762306a36Sopenharmony_ci return; 509862306a36Sopenharmony_ci 509962306a36Sopenharmony_ci index = srcu_read_lock(&ipmi_interfaces_srcu); 510062306a36Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 510162306a36Sopenharmony_ci if (atomic_read(&intf->event_waiters)) { 510262306a36Sopenharmony_ci intf->ticks_to_req_ev--; 510362306a36Sopenharmony_ci if (intf->ticks_to_req_ev == 0) { 510462306a36Sopenharmony_ci ipmi_request_event(intf); 510562306a36Sopenharmony_ci intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; 510662306a36Sopenharmony_ci } 510762306a36Sopenharmony_ci need_timer = true; 510862306a36Sopenharmony_ci } 510962306a36Sopenharmony_ci 511062306a36Sopenharmony_ci need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); 511162306a36Sopenharmony_ci } 511262306a36Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 511362306a36Sopenharmony_ci 511462306a36Sopenharmony_ci if (need_timer) 511562306a36Sopenharmony_ci mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); 511662306a36Sopenharmony_ci} 511762306a36Sopenharmony_ci 511862306a36Sopenharmony_cistatic void need_waiter(struct ipmi_smi *intf) 511962306a36Sopenharmony_ci{ 512062306a36Sopenharmony_ci /* Racy, but worst case we start the timer twice. */ 512162306a36Sopenharmony_ci if (!timer_pending(&ipmi_timer)) 512262306a36Sopenharmony_ci mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); 512362306a36Sopenharmony_ci} 512462306a36Sopenharmony_ci 512562306a36Sopenharmony_cistatic atomic_t smi_msg_inuse_count = ATOMIC_INIT(0); 512662306a36Sopenharmony_cistatic atomic_t recv_msg_inuse_count = ATOMIC_INIT(0); 512762306a36Sopenharmony_ci 512862306a36Sopenharmony_cistatic void free_smi_msg(struct ipmi_smi_msg *msg) 512962306a36Sopenharmony_ci{ 513062306a36Sopenharmony_ci atomic_dec(&smi_msg_inuse_count); 513162306a36Sopenharmony_ci /* Try to keep as much stuff out of the panic path as possible. */ 513262306a36Sopenharmony_ci if (!oops_in_progress) 513362306a36Sopenharmony_ci kfree(msg); 513462306a36Sopenharmony_ci} 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_cistruct ipmi_smi_msg *ipmi_alloc_smi_msg(void) 513762306a36Sopenharmony_ci{ 513862306a36Sopenharmony_ci struct ipmi_smi_msg *rv; 513962306a36Sopenharmony_ci rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC); 514062306a36Sopenharmony_ci if (rv) { 514162306a36Sopenharmony_ci rv->done = free_smi_msg; 514262306a36Sopenharmony_ci rv->user_data = NULL; 514362306a36Sopenharmony_ci rv->type = IPMI_SMI_MSG_TYPE_NORMAL; 514462306a36Sopenharmony_ci atomic_inc(&smi_msg_inuse_count); 514562306a36Sopenharmony_ci } 514662306a36Sopenharmony_ci return rv; 514762306a36Sopenharmony_ci} 514862306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_alloc_smi_msg); 514962306a36Sopenharmony_ci 515062306a36Sopenharmony_cistatic void free_recv_msg(struct ipmi_recv_msg *msg) 515162306a36Sopenharmony_ci{ 515262306a36Sopenharmony_ci atomic_dec(&recv_msg_inuse_count); 515362306a36Sopenharmony_ci /* Try to keep as much stuff out of the panic path as possible. */ 515462306a36Sopenharmony_ci if (!oops_in_progress) 515562306a36Sopenharmony_ci kfree(msg); 515662306a36Sopenharmony_ci} 515762306a36Sopenharmony_ci 515862306a36Sopenharmony_cistatic struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) 515962306a36Sopenharmony_ci{ 516062306a36Sopenharmony_ci struct ipmi_recv_msg *rv; 516162306a36Sopenharmony_ci 516262306a36Sopenharmony_ci rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC); 516362306a36Sopenharmony_ci if (rv) { 516462306a36Sopenharmony_ci rv->user = NULL; 516562306a36Sopenharmony_ci rv->done = free_recv_msg; 516662306a36Sopenharmony_ci atomic_inc(&recv_msg_inuse_count); 516762306a36Sopenharmony_ci } 516862306a36Sopenharmony_ci return rv; 516962306a36Sopenharmony_ci} 517062306a36Sopenharmony_ci 517162306a36Sopenharmony_civoid ipmi_free_recv_msg(struct ipmi_recv_msg *msg) 517262306a36Sopenharmony_ci{ 517362306a36Sopenharmony_ci if (msg->user && !oops_in_progress) 517462306a36Sopenharmony_ci kref_put(&msg->user->refcount, free_user); 517562306a36Sopenharmony_ci msg->done(msg); 517662306a36Sopenharmony_ci} 517762306a36Sopenharmony_ciEXPORT_SYMBOL(ipmi_free_recv_msg); 517862306a36Sopenharmony_ci 517962306a36Sopenharmony_cistatic atomic_t panic_done_count = ATOMIC_INIT(0); 518062306a36Sopenharmony_ci 518162306a36Sopenharmony_cistatic void dummy_smi_done_handler(struct ipmi_smi_msg *msg) 518262306a36Sopenharmony_ci{ 518362306a36Sopenharmony_ci atomic_dec(&panic_done_count); 518462306a36Sopenharmony_ci} 518562306a36Sopenharmony_ci 518662306a36Sopenharmony_cistatic void dummy_recv_done_handler(struct ipmi_recv_msg *msg) 518762306a36Sopenharmony_ci{ 518862306a36Sopenharmony_ci atomic_dec(&panic_done_count); 518962306a36Sopenharmony_ci} 519062306a36Sopenharmony_ci 519162306a36Sopenharmony_ci/* 519262306a36Sopenharmony_ci * Inside a panic, send a message and wait for a response. 519362306a36Sopenharmony_ci */ 519462306a36Sopenharmony_cistatic void ipmi_panic_request_and_wait(struct ipmi_smi *intf, 519562306a36Sopenharmony_ci struct ipmi_addr *addr, 519662306a36Sopenharmony_ci struct kernel_ipmi_msg *msg) 519762306a36Sopenharmony_ci{ 519862306a36Sopenharmony_ci struct ipmi_smi_msg smi_msg; 519962306a36Sopenharmony_ci struct ipmi_recv_msg recv_msg; 520062306a36Sopenharmony_ci int rv; 520162306a36Sopenharmony_ci 520262306a36Sopenharmony_ci smi_msg.done = dummy_smi_done_handler; 520362306a36Sopenharmony_ci recv_msg.done = dummy_recv_done_handler; 520462306a36Sopenharmony_ci atomic_add(2, &panic_done_count); 520562306a36Sopenharmony_ci rv = i_ipmi_request(NULL, 520662306a36Sopenharmony_ci intf, 520762306a36Sopenharmony_ci addr, 520862306a36Sopenharmony_ci 0, 520962306a36Sopenharmony_ci msg, 521062306a36Sopenharmony_ci intf, 521162306a36Sopenharmony_ci &smi_msg, 521262306a36Sopenharmony_ci &recv_msg, 521362306a36Sopenharmony_ci 0, 521462306a36Sopenharmony_ci intf->addrinfo[0].address, 521562306a36Sopenharmony_ci intf->addrinfo[0].lun, 521662306a36Sopenharmony_ci 0, 1); /* Don't retry, and don't wait. */ 521762306a36Sopenharmony_ci if (rv) 521862306a36Sopenharmony_ci atomic_sub(2, &panic_done_count); 521962306a36Sopenharmony_ci else if (intf->handlers->flush_messages) 522062306a36Sopenharmony_ci intf->handlers->flush_messages(intf->send_info); 522162306a36Sopenharmony_ci 522262306a36Sopenharmony_ci while (atomic_read(&panic_done_count) != 0) 522362306a36Sopenharmony_ci ipmi_poll(intf); 522462306a36Sopenharmony_ci} 522562306a36Sopenharmony_ci 522662306a36Sopenharmony_cistatic void event_receiver_fetcher(struct ipmi_smi *intf, 522762306a36Sopenharmony_ci struct ipmi_recv_msg *msg) 522862306a36Sopenharmony_ci{ 522962306a36Sopenharmony_ci if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 523062306a36Sopenharmony_ci && (msg->msg.netfn == IPMI_NETFN_SENSOR_EVENT_RESPONSE) 523162306a36Sopenharmony_ci && (msg->msg.cmd == IPMI_GET_EVENT_RECEIVER_CMD) 523262306a36Sopenharmony_ci && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { 523362306a36Sopenharmony_ci /* A get event receiver command, save it. */ 523462306a36Sopenharmony_ci intf->event_receiver = msg->msg.data[1]; 523562306a36Sopenharmony_ci intf->event_receiver_lun = msg->msg.data[2] & 0x3; 523662306a36Sopenharmony_ci } 523762306a36Sopenharmony_ci} 523862306a36Sopenharmony_ci 523962306a36Sopenharmony_cistatic void device_id_fetcher(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) 524062306a36Sopenharmony_ci{ 524162306a36Sopenharmony_ci if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 524262306a36Sopenharmony_ci && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) 524362306a36Sopenharmony_ci && (msg->msg.cmd == IPMI_GET_DEVICE_ID_CMD) 524462306a36Sopenharmony_ci && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { 524562306a36Sopenharmony_ci /* 524662306a36Sopenharmony_ci * A get device id command, save if we are an event 524762306a36Sopenharmony_ci * receiver or generator. 524862306a36Sopenharmony_ci */ 524962306a36Sopenharmony_ci intf->local_sel_device = (msg->msg.data[6] >> 2) & 1; 525062306a36Sopenharmony_ci intf->local_event_generator = (msg->msg.data[6] >> 5) & 1; 525162306a36Sopenharmony_ci } 525262306a36Sopenharmony_ci} 525362306a36Sopenharmony_ci 525462306a36Sopenharmony_cistatic void send_panic_events(struct ipmi_smi *intf, char *str) 525562306a36Sopenharmony_ci{ 525662306a36Sopenharmony_ci struct kernel_ipmi_msg msg; 525762306a36Sopenharmony_ci unsigned char data[16]; 525862306a36Sopenharmony_ci struct ipmi_system_interface_addr *si; 525962306a36Sopenharmony_ci struct ipmi_addr addr; 526062306a36Sopenharmony_ci char *p = str; 526162306a36Sopenharmony_ci struct ipmi_ipmb_addr *ipmb; 526262306a36Sopenharmony_ci int j; 526362306a36Sopenharmony_ci 526462306a36Sopenharmony_ci if (ipmi_send_panic_event == IPMI_SEND_PANIC_EVENT_NONE) 526562306a36Sopenharmony_ci return; 526662306a36Sopenharmony_ci 526762306a36Sopenharmony_ci si = (struct ipmi_system_interface_addr *) &addr; 526862306a36Sopenharmony_ci si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 526962306a36Sopenharmony_ci si->channel = IPMI_BMC_CHANNEL; 527062306a36Sopenharmony_ci si->lun = 0; 527162306a36Sopenharmony_ci 527262306a36Sopenharmony_ci /* Fill in an event telling that we have failed. */ 527362306a36Sopenharmony_ci msg.netfn = 0x04; /* Sensor or Event. */ 527462306a36Sopenharmony_ci msg.cmd = 2; /* Platform event command. */ 527562306a36Sopenharmony_ci msg.data = data; 527662306a36Sopenharmony_ci msg.data_len = 8; 527762306a36Sopenharmony_ci data[0] = 0x41; /* Kernel generator ID, IPMI table 5-4 */ 527862306a36Sopenharmony_ci data[1] = 0x03; /* This is for IPMI 1.0. */ 527962306a36Sopenharmony_ci data[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */ 528062306a36Sopenharmony_ci data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */ 528162306a36Sopenharmony_ci data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */ 528262306a36Sopenharmony_ci 528362306a36Sopenharmony_ci /* 528462306a36Sopenharmony_ci * Put a few breadcrumbs in. Hopefully later we can add more things 528562306a36Sopenharmony_ci * to make the panic events more useful. 528662306a36Sopenharmony_ci */ 528762306a36Sopenharmony_ci if (str) { 528862306a36Sopenharmony_ci data[3] = str[0]; 528962306a36Sopenharmony_ci data[6] = str[1]; 529062306a36Sopenharmony_ci data[7] = str[2]; 529162306a36Sopenharmony_ci } 529262306a36Sopenharmony_ci 529362306a36Sopenharmony_ci /* Send the event announcing the panic. */ 529462306a36Sopenharmony_ci ipmi_panic_request_and_wait(intf, &addr, &msg); 529562306a36Sopenharmony_ci 529662306a36Sopenharmony_ci /* 529762306a36Sopenharmony_ci * On every interface, dump a bunch of OEM event holding the 529862306a36Sopenharmony_ci * string. 529962306a36Sopenharmony_ci */ 530062306a36Sopenharmony_ci if (ipmi_send_panic_event != IPMI_SEND_PANIC_EVENT_STRING || !str) 530162306a36Sopenharmony_ci return; 530262306a36Sopenharmony_ci 530362306a36Sopenharmony_ci /* 530462306a36Sopenharmony_ci * intf_num is used as an marker to tell if the 530562306a36Sopenharmony_ci * interface is valid. Thus we need a read barrier to 530662306a36Sopenharmony_ci * make sure data fetched before checking intf_num 530762306a36Sopenharmony_ci * won't be used. 530862306a36Sopenharmony_ci */ 530962306a36Sopenharmony_ci smp_rmb(); 531062306a36Sopenharmony_ci 531162306a36Sopenharmony_ci /* 531262306a36Sopenharmony_ci * First job here is to figure out where to send the 531362306a36Sopenharmony_ci * OEM events. There's no way in IPMI to send OEM 531462306a36Sopenharmony_ci * events using an event send command, so we have to 531562306a36Sopenharmony_ci * find the SEL to put them in and stick them in 531662306a36Sopenharmony_ci * there. 531762306a36Sopenharmony_ci */ 531862306a36Sopenharmony_ci 531962306a36Sopenharmony_ci /* Get capabilities from the get device id. */ 532062306a36Sopenharmony_ci intf->local_sel_device = 0; 532162306a36Sopenharmony_ci intf->local_event_generator = 0; 532262306a36Sopenharmony_ci intf->event_receiver = 0; 532362306a36Sopenharmony_ci 532462306a36Sopenharmony_ci /* Request the device info from the local MC. */ 532562306a36Sopenharmony_ci msg.netfn = IPMI_NETFN_APP_REQUEST; 532662306a36Sopenharmony_ci msg.cmd = IPMI_GET_DEVICE_ID_CMD; 532762306a36Sopenharmony_ci msg.data = NULL; 532862306a36Sopenharmony_ci msg.data_len = 0; 532962306a36Sopenharmony_ci intf->null_user_handler = device_id_fetcher; 533062306a36Sopenharmony_ci ipmi_panic_request_and_wait(intf, &addr, &msg); 533162306a36Sopenharmony_ci 533262306a36Sopenharmony_ci if (intf->local_event_generator) { 533362306a36Sopenharmony_ci /* Request the event receiver from the local MC. */ 533462306a36Sopenharmony_ci msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST; 533562306a36Sopenharmony_ci msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD; 533662306a36Sopenharmony_ci msg.data = NULL; 533762306a36Sopenharmony_ci msg.data_len = 0; 533862306a36Sopenharmony_ci intf->null_user_handler = event_receiver_fetcher; 533962306a36Sopenharmony_ci ipmi_panic_request_and_wait(intf, &addr, &msg); 534062306a36Sopenharmony_ci } 534162306a36Sopenharmony_ci intf->null_user_handler = NULL; 534262306a36Sopenharmony_ci 534362306a36Sopenharmony_ci /* 534462306a36Sopenharmony_ci * Validate the event receiver. The low bit must not 534562306a36Sopenharmony_ci * be 1 (it must be a valid IPMB address), it cannot 534662306a36Sopenharmony_ci * be zero, and it must not be my address. 534762306a36Sopenharmony_ci */ 534862306a36Sopenharmony_ci if (((intf->event_receiver & 1) == 0) 534962306a36Sopenharmony_ci && (intf->event_receiver != 0) 535062306a36Sopenharmony_ci && (intf->event_receiver != intf->addrinfo[0].address)) { 535162306a36Sopenharmony_ci /* 535262306a36Sopenharmony_ci * The event receiver is valid, send an IPMB 535362306a36Sopenharmony_ci * message. 535462306a36Sopenharmony_ci */ 535562306a36Sopenharmony_ci ipmb = (struct ipmi_ipmb_addr *) &addr; 535662306a36Sopenharmony_ci ipmb->addr_type = IPMI_IPMB_ADDR_TYPE; 535762306a36Sopenharmony_ci ipmb->channel = 0; /* FIXME - is this right? */ 535862306a36Sopenharmony_ci ipmb->lun = intf->event_receiver_lun; 535962306a36Sopenharmony_ci ipmb->slave_addr = intf->event_receiver; 536062306a36Sopenharmony_ci } else if (intf->local_sel_device) { 536162306a36Sopenharmony_ci /* 536262306a36Sopenharmony_ci * The event receiver was not valid (or was 536362306a36Sopenharmony_ci * me), but I am an SEL device, just dump it 536462306a36Sopenharmony_ci * in my SEL. 536562306a36Sopenharmony_ci */ 536662306a36Sopenharmony_ci si = (struct ipmi_system_interface_addr *) &addr; 536762306a36Sopenharmony_ci si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 536862306a36Sopenharmony_ci si->channel = IPMI_BMC_CHANNEL; 536962306a36Sopenharmony_ci si->lun = 0; 537062306a36Sopenharmony_ci } else 537162306a36Sopenharmony_ci return; /* No where to send the event. */ 537262306a36Sopenharmony_ci 537362306a36Sopenharmony_ci msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */ 537462306a36Sopenharmony_ci msg.cmd = IPMI_ADD_SEL_ENTRY_CMD; 537562306a36Sopenharmony_ci msg.data = data; 537662306a36Sopenharmony_ci msg.data_len = 16; 537762306a36Sopenharmony_ci 537862306a36Sopenharmony_ci j = 0; 537962306a36Sopenharmony_ci while (*p) { 538062306a36Sopenharmony_ci int size = strlen(p); 538162306a36Sopenharmony_ci 538262306a36Sopenharmony_ci if (size > 11) 538362306a36Sopenharmony_ci size = 11; 538462306a36Sopenharmony_ci data[0] = 0; 538562306a36Sopenharmony_ci data[1] = 0; 538662306a36Sopenharmony_ci data[2] = 0xf0; /* OEM event without timestamp. */ 538762306a36Sopenharmony_ci data[3] = intf->addrinfo[0].address; 538862306a36Sopenharmony_ci data[4] = j++; /* sequence # */ 538962306a36Sopenharmony_ci /* 539062306a36Sopenharmony_ci * Always give 11 bytes, so strncpy will fill 539162306a36Sopenharmony_ci * it with zeroes for me. 539262306a36Sopenharmony_ci */ 539362306a36Sopenharmony_ci strncpy(data+5, p, 11); 539462306a36Sopenharmony_ci p += size; 539562306a36Sopenharmony_ci 539662306a36Sopenharmony_ci ipmi_panic_request_and_wait(intf, &addr, &msg); 539762306a36Sopenharmony_ci } 539862306a36Sopenharmony_ci} 539962306a36Sopenharmony_ci 540062306a36Sopenharmony_cistatic int has_panicked; 540162306a36Sopenharmony_ci 540262306a36Sopenharmony_cistatic int panic_event(struct notifier_block *this, 540362306a36Sopenharmony_ci unsigned long event, 540462306a36Sopenharmony_ci void *ptr) 540562306a36Sopenharmony_ci{ 540662306a36Sopenharmony_ci struct ipmi_smi *intf; 540762306a36Sopenharmony_ci struct ipmi_user *user; 540862306a36Sopenharmony_ci 540962306a36Sopenharmony_ci if (has_panicked) 541062306a36Sopenharmony_ci return NOTIFY_DONE; 541162306a36Sopenharmony_ci has_panicked = 1; 541262306a36Sopenharmony_ci 541362306a36Sopenharmony_ci /* For every registered interface, set it to run to completion. */ 541462306a36Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 541562306a36Sopenharmony_ci if (!intf->handlers || intf->intf_num == -1) 541662306a36Sopenharmony_ci /* Interface is not ready. */ 541762306a36Sopenharmony_ci continue; 541862306a36Sopenharmony_ci 541962306a36Sopenharmony_ci if (!intf->handlers->poll) 542062306a36Sopenharmony_ci continue; 542162306a36Sopenharmony_ci 542262306a36Sopenharmony_ci /* 542362306a36Sopenharmony_ci * If we were interrupted while locking xmit_msgs_lock or 542462306a36Sopenharmony_ci * waiting_rcv_msgs_lock, the corresponding list may be 542562306a36Sopenharmony_ci * corrupted. In this case, drop items on the list for 542662306a36Sopenharmony_ci * the safety. 542762306a36Sopenharmony_ci */ 542862306a36Sopenharmony_ci if (!spin_trylock(&intf->xmit_msgs_lock)) { 542962306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->xmit_msgs); 543062306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->hp_xmit_msgs); 543162306a36Sopenharmony_ci } else 543262306a36Sopenharmony_ci spin_unlock(&intf->xmit_msgs_lock); 543362306a36Sopenharmony_ci 543462306a36Sopenharmony_ci if (!spin_trylock(&intf->waiting_rcv_msgs_lock)) 543562306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->waiting_rcv_msgs); 543662306a36Sopenharmony_ci else 543762306a36Sopenharmony_ci spin_unlock(&intf->waiting_rcv_msgs_lock); 543862306a36Sopenharmony_ci 543962306a36Sopenharmony_ci intf->run_to_completion = 1; 544062306a36Sopenharmony_ci if (intf->handlers->set_run_to_completion) 544162306a36Sopenharmony_ci intf->handlers->set_run_to_completion(intf->send_info, 544262306a36Sopenharmony_ci 1); 544362306a36Sopenharmony_ci 544462306a36Sopenharmony_ci list_for_each_entry_rcu(user, &intf->users, link) { 544562306a36Sopenharmony_ci if (user->handler->ipmi_panic_handler) 544662306a36Sopenharmony_ci user->handler->ipmi_panic_handler( 544762306a36Sopenharmony_ci user->handler_data); 544862306a36Sopenharmony_ci } 544962306a36Sopenharmony_ci 545062306a36Sopenharmony_ci send_panic_events(intf, ptr); 545162306a36Sopenharmony_ci } 545262306a36Sopenharmony_ci 545362306a36Sopenharmony_ci return NOTIFY_DONE; 545462306a36Sopenharmony_ci} 545562306a36Sopenharmony_ci 545662306a36Sopenharmony_ci/* Must be called with ipmi_interfaces_mutex held. */ 545762306a36Sopenharmony_cistatic int ipmi_register_driver(void) 545862306a36Sopenharmony_ci{ 545962306a36Sopenharmony_ci int rv; 546062306a36Sopenharmony_ci 546162306a36Sopenharmony_ci if (drvregistered) 546262306a36Sopenharmony_ci return 0; 546362306a36Sopenharmony_ci 546462306a36Sopenharmony_ci rv = driver_register(&ipmidriver.driver); 546562306a36Sopenharmony_ci if (rv) 546662306a36Sopenharmony_ci pr_err("Could not register IPMI driver\n"); 546762306a36Sopenharmony_ci else 546862306a36Sopenharmony_ci drvregistered = true; 546962306a36Sopenharmony_ci return rv; 547062306a36Sopenharmony_ci} 547162306a36Sopenharmony_ci 547262306a36Sopenharmony_cistatic struct notifier_block panic_block = { 547362306a36Sopenharmony_ci .notifier_call = panic_event, 547462306a36Sopenharmony_ci .next = NULL, 547562306a36Sopenharmony_ci .priority = 200 /* priority: INT_MAX >= x >= 0 */ 547662306a36Sopenharmony_ci}; 547762306a36Sopenharmony_ci 547862306a36Sopenharmony_cistatic int ipmi_init_msghandler(void) 547962306a36Sopenharmony_ci{ 548062306a36Sopenharmony_ci int rv; 548162306a36Sopenharmony_ci 548262306a36Sopenharmony_ci mutex_lock(&ipmi_interfaces_mutex); 548362306a36Sopenharmony_ci rv = ipmi_register_driver(); 548462306a36Sopenharmony_ci if (rv) 548562306a36Sopenharmony_ci goto out; 548662306a36Sopenharmony_ci if (initialized) 548762306a36Sopenharmony_ci goto out; 548862306a36Sopenharmony_ci 548962306a36Sopenharmony_ci rv = init_srcu_struct(&ipmi_interfaces_srcu); 549062306a36Sopenharmony_ci if (rv) 549162306a36Sopenharmony_ci goto out; 549262306a36Sopenharmony_ci 549362306a36Sopenharmony_ci remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq"); 549462306a36Sopenharmony_ci if (!remove_work_wq) { 549562306a36Sopenharmony_ci pr_err("unable to create ipmi-msghandler-remove-wq workqueue"); 549662306a36Sopenharmony_ci rv = -ENOMEM; 549762306a36Sopenharmony_ci goto out_wq; 549862306a36Sopenharmony_ci } 549962306a36Sopenharmony_ci 550062306a36Sopenharmony_ci timer_setup(&ipmi_timer, ipmi_timeout, 0); 550162306a36Sopenharmony_ci mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); 550262306a36Sopenharmony_ci 550362306a36Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, &panic_block); 550462306a36Sopenharmony_ci 550562306a36Sopenharmony_ci initialized = true; 550662306a36Sopenharmony_ci 550762306a36Sopenharmony_ciout_wq: 550862306a36Sopenharmony_ci if (rv) 550962306a36Sopenharmony_ci cleanup_srcu_struct(&ipmi_interfaces_srcu); 551062306a36Sopenharmony_ciout: 551162306a36Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 551262306a36Sopenharmony_ci return rv; 551362306a36Sopenharmony_ci} 551462306a36Sopenharmony_ci 551562306a36Sopenharmony_cistatic int __init ipmi_init_msghandler_mod(void) 551662306a36Sopenharmony_ci{ 551762306a36Sopenharmony_ci int rv; 551862306a36Sopenharmony_ci 551962306a36Sopenharmony_ci pr_info("version " IPMI_DRIVER_VERSION "\n"); 552062306a36Sopenharmony_ci 552162306a36Sopenharmony_ci mutex_lock(&ipmi_interfaces_mutex); 552262306a36Sopenharmony_ci rv = ipmi_register_driver(); 552362306a36Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 552462306a36Sopenharmony_ci 552562306a36Sopenharmony_ci return rv; 552662306a36Sopenharmony_ci} 552762306a36Sopenharmony_ci 552862306a36Sopenharmony_cistatic void __exit cleanup_ipmi(void) 552962306a36Sopenharmony_ci{ 553062306a36Sopenharmony_ci int count; 553162306a36Sopenharmony_ci 553262306a36Sopenharmony_ci if (initialized) { 553362306a36Sopenharmony_ci destroy_workqueue(remove_work_wq); 553462306a36Sopenharmony_ci 553562306a36Sopenharmony_ci atomic_notifier_chain_unregister(&panic_notifier_list, 553662306a36Sopenharmony_ci &panic_block); 553762306a36Sopenharmony_ci 553862306a36Sopenharmony_ci /* 553962306a36Sopenharmony_ci * This can't be called if any interfaces exist, so no worry 554062306a36Sopenharmony_ci * about shutting down the interfaces. 554162306a36Sopenharmony_ci */ 554262306a36Sopenharmony_ci 554362306a36Sopenharmony_ci /* 554462306a36Sopenharmony_ci * Tell the timer to stop, then wait for it to stop. This 554562306a36Sopenharmony_ci * avoids problems with race conditions removing the timer 554662306a36Sopenharmony_ci * here. 554762306a36Sopenharmony_ci */ 554862306a36Sopenharmony_ci atomic_set(&stop_operation, 1); 554962306a36Sopenharmony_ci del_timer_sync(&ipmi_timer); 555062306a36Sopenharmony_ci 555162306a36Sopenharmony_ci initialized = false; 555262306a36Sopenharmony_ci 555362306a36Sopenharmony_ci /* Check for buffer leaks. */ 555462306a36Sopenharmony_ci count = atomic_read(&smi_msg_inuse_count); 555562306a36Sopenharmony_ci if (count != 0) 555662306a36Sopenharmony_ci pr_warn("SMI message count %d at exit\n", count); 555762306a36Sopenharmony_ci count = atomic_read(&recv_msg_inuse_count); 555862306a36Sopenharmony_ci if (count != 0) 555962306a36Sopenharmony_ci pr_warn("recv message count %d at exit\n", count); 556062306a36Sopenharmony_ci 556162306a36Sopenharmony_ci cleanup_srcu_struct(&ipmi_interfaces_srcu); 556262306a36Sopenharmony_ci } 556362306a36Sopenharmony_ci if (drvregistered) 556462306a36Sopenharmony_ci driver_unregister(&ipmidriver.driver); 556562306a36Sopenharmony_ci} 556662306a36Sopenharmony_cimodule_exit(cleanup_ipmi); 556762306a36Sopenharmony_ci 556862306a36Sopenharmony_cimodule_init(ipmi_init_msghandler_mod); 556962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 557062306a36Sopenharmony_ciMODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); 557162306a36Sopenharmony_ciMODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI interface."); 557262306a36Sopenharmony_ciMODULE_VERSION(IPMI_DRIVER_VERSION); 557362306a36Sopenharmony_ciMODULE_SOFTDEP("post: ipmi_devintf"); 5574