18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ipmi_msghandler.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Incoming and outgoing message routing for an IPMI interface. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: MontaVista Software, Inc. 88c2ecf20Sopenharmony_ci * Corey Minyard <minyard@mvista.com> 98c2ecf20Sopenharmony_ci * source@mvista.com 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright 2002 MontaVista Software Inc. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPMI message handler: " fmt 158c2ecf20Sopenharmony_ci#define dev_fmt(fmt) pr_fmt(fmt) 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/poll.h> 208c2ecf20Sopenharmony_ci#include <linux/sched.h> 218c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci#include <linux/mutex.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/ipmi.h> 268c2ecf20Sopenharmony_ci#include <linux/ipmi_smi.h> 278c2ecf20Sopenharmony_ci#include <linux/notifier.h> 288c2ecf20Sopenharmony_ci#include <linux/init.h> 298c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 308c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 318c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 328c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 338c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 348c2ecf20Sopenharmony_ci#include <linux/uuid.h> 358c2ecf20Sopenharmony_ci#include <linux/nospec.h> 368c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 378c2ecf20Sopenharmony_ci#include <linux/delay.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define IPMI_DRIVER_VERSION "39.2" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); 428c2ecf20Sopenharmony_cistatic int ipmi_init_msghandler(void); 438c2ecf20Sopenharmony_cistatic void smi_recv_tasklet(struct tasklet_struct *t); 448c2ecf20Sopenharmony_cistatic void handle_new_recv_msgs(struct ipmi_smi *intf); 458c2ecf20Sopenharmony_cistatic void need_waiter(struct ipmi_smi *intf); 468c2ecf20Sopenharmony_cistatic int handle_one_recv_msg(struct ipmi_smi *intf, 478c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic bool initialized; 508c2ecf20Sopenharmony_cistatic bool drvregistered; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cienum ipmi_panic_event_op { 538c2ecf20Sopenharmony_ci IPMI_SEND_PANIC_EVENT_NONE, 548c2ecf20Sopenharmony_ci IPMI_SEND_PANIC_EVENT, 558c2ecf20Sopenharmony_ci IPMI_SEND_PANIC_EVENT_STRING 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci#ifdef CONFIG_IPMI_PANIC_STRING 588c2ecf20Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_STRING 598c2ecf20Sopenharmony_ci#elif defined(CONFIG_IPMI_PANIC_EVENT) 608c2ecf20Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT 618c2ecf20Sopenharmony_ci#else 628c2ecf20Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_NONE 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic enum ipmi_panic_event_op ipmi_send_panic_event = IPMI_PANIC_DEFAULT; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int panic_op_write_handler(const char *val, 688c2ecf20Sopenharmony_ci const struct kernel_param *kp) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci char valcp[16]; 718c2ecf20Sopenharmony_ci char *s; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci strncpy(valcp, val, 15); 748c2ecf20Sopenharmony_ci valcp[15] = '\0'; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci s = strstrip(valcp); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (strcmp(s, "none") == 0) 798c2ecf20Sopenharmony_ci ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_NONE; 808c2ecf20Sopenharmony_ci else if (strcmp(s, "event") == 0) 818c2ecf20Sopenharmony_ci ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT; 828c2ecf20Sopenharmony_ci else if (strcmp(s, "string") == 0) 838c2ecf20Sopenharmony_ci ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_STRING; 848c2ecf20Sopenharmony_ci else 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int panic_op_read_handler(char *buffer, const struct kernel_param *kp) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci switch (ipmi_send_panic_event) { 938c2ecf20Sopenharmony_ci case IPMI_SEND_PANIC_EVENT_NONE: 948c2ecf20Sopenharmony_ci strcpy(buffer, "none\n"); 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci case IPMI_SEND_PANIC_EVENT: 988c2ecf20Sopenharmony_ci strcpy(buffer, "event\n"); 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci case IPMI_SEND_PANIC_EVENT_STRING: 1028c2ecf20Sopenharmony_ci strcpy(buffer, "string\n"); 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci default: 1068c2ecf20Sopenharmony_ci strcpy(buffer, "???\n"); 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return strlen(buffer); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic const struct kernel_param_ops panic_op_ops = { 1148c2ecf20Sopenharmony_ci .set = panic_op_write_handler, 1158c2ecf20Sopenharmony_ci .get = panic_op_read_handler 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_cimodule_param_cb(panic_op, &panic_op_ops, NULL, 0600); 1188c2ecf20Sopenharmony_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."); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define MAX_EVENTS_IN_QUEUE 25 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* Remain in auto-maintenance mode for this amount of time (in ms). */ 1248c2ecf20Sopenharmony_cistatic unsigned long maintenance_mode_timeout_ms = 30000; 1258c2ecf20Sopenharmony_cimodule_param(maintenance_mode_timeout_ms, ulong, 0644); 1268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maintenance_mode_timeout_ms, 1278c2ecf20Sopenharmony_ci "The time (milliseconds) after the last maintenance message that the connection stays in maintenance mode."); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * Don't let a message sit in a queue forever, always time it with at lest 1318c2ecf20Sopenharmony_ci * the max message timer. This is in milliseconds. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci#define MAX_MSG_TIMEOUT 60000 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * Timeout times below are in milliseconds, and are done off a 1 1378c2ecf20Sopenharmony_ci * second timer. So setting the value to 1000 would mean anything 1388c2ecf20Sopenharmony_ci * between 0 and 1000ms. So really the only reasonable minimum 1398c2ecf20Sopenharmony_ci * setting it 2000ms, which is between 1 and 2 seconds. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* The default timeout for message retries. */ 1438c2ecf20Sopenharmony_cistatic unsigned long default_retry_ms = 2000; 1448c2ecf20Sopenharmony_cimodule_param(default_retry_ms, ulong, 0644); 1458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_retry_ms, 1468c2ecf20Sopenharmony_ci "The time (milliseconds) between retry sends"); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* The default timeout for maintenance mode message retries. */ 1498c2ecf20Sopenharmony_cistatic unsigned long default_maintenance_retry_ms = 3000; 1508c2ecf20Sopenharmony_cimodule_param(default_maintenance_retry_ms, ulong, 0644); 1518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_maintenance_retry_ms, 1528c2ecf20Sopenharmony_ci "The time (milliseconds) between retry sends in maintenance mode"); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* The default maximum number of retries */ 1558c2ecf20Sopenharmony_cistatic unsigned int default_max_retries = 4; 1568c2ecf20Sopenharmony_cimodule_param(default_max_retries, uint, 0644); 1578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_max_retries, 1588c2ecf20Sopenharmony_ci "The time (milliseconds) between retry sends in maintenance mode"); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* Call every ~1000 ms. */ 1618c2ecf20Sopenharmony_ci#define IPMI_TIMEOUT_TIME 1000 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* How many jiffies does it take to get to the timeout time. */ 1648c2ecf20Sopenharmony_ci#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* 1678c2ecf20Sopenharmony_ci * Request events from the queue every second (this is the number of 1688c2ecf20Sopenharmony_ci * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the 1698c2ecf20Sopenharmony_ci * future, IPMI will add a way to know immediately if an event is in 1708c2ecf20Sopenharmony_ci * the queue and this silliness can go away. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* How long should we cache dynamic device IDs? */ 1758c2ecf20Sopenharmony_ci#define IPMI_DYN_DEV_ID_EXPIRY (10 * HZ) 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* 1788c2ecf20Sopenharmony_ci * The main "user" data structure. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_cistruct ipmi_user { 1818c2ecf20Sopenharmony_ci struct list_head link; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * Set to NULL when the user is destroyed, a pointer to myself 1858c2ecf20Sopenharmony_ci * so srcu_dereference can be used on it. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci struct ipmi_user *self; 1888c2ecf20Sopenharmony_ci struct srcu_struct release_barrier; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci struct kref refcount; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* The upper layer that handles receive messages. */ 1938c2ecf20Sopenharmony_ci const struct ipmi_user_hndl *handler; 1948c2ecf20Sopenharmony_ci void *handler_data; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* The interface this user is bound to. */ 1978c2ecf20Sopenharmony_ci struct ipmi_smi *intf; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Does this interface receive IPMI events? */ 2008c2ecf20Sopenharmony_ci bool gets_events; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Free must run in process context for RCU cleanup. */ 2038c2ecf20Sopenharmony_ci struct work_struct remove_work; 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic struct workqueue_struct *remove_work_wq; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user, int *index) 2098c2ecf20Sopenharmony_ci __acquires(user->release_barrier) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct ipmi_user *ruser; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci *index = srcu_read_lock(&user->release_barrier); 2148c2ecf20Sopenharmony_ci ruser = srcu_dereference(user->self, &user->release_barrier); 2158c2ecf20Sopenharmony_ci if (!ruser) 2168c2ecf20Sopenharmony_ci srcu_read_unlock(&user->release_barrier, *index); 2178c2ecf20Sopenharmony_ci return ruser; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void release_ipmi_user(struct ipmi_user *user, int index) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci srcu_read_unlock(&user->release_barrier, index); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistruct cmd_rcvr { 2268c2ecf20Sopenharmony_ci struct list_head link; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci struct ipmi_user *user; 2298c2ecf20Sopenharmony_ci unsigned char netfn; 2308c2ecf20Sopenharmony_ci unsigned char cmd; 2318c2ecf20Sopenharmony_ci unsigned int chans; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * This is used to form a linked lised during mass deletion. 2358c2ecf20Sopenharmony_ci * Since this is in an RCU list, we cannot use the link above 2368c2ecf20Sopenharmony_ci * or change any data until the RCU period completes. So we 2378c2ecf20Sopenharmony_ci * use this next variable during mass deletion so we can have 2388c2ecf20Sopenharmony_ci * a list and don't have to wait and restart the search on 2398c2ecf20Sopenharmony_ci * every individual deletion of a command. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci struct cmd_rcvr *next; 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistruct seq_table { 2458c2ecf20Sopenharmony_ci unsigned int inuse : 1; 2468c2ecf20Sopenharmony_ci unsigned int broadcast : 1; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci unsigned long timeout; 2498c2ecf20Sopenharmony_ci unsigned long orig_timeout; 2508c2ecf20Sopenharmony_ci unsigned int retries_left; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * To verify on an incoming send message response that this is 2548c2ecf20Sopenharmony_ci * the message that the response is for, we keep a sequence id 2558c2ecf20Sopenharmony_ci * and increment it every time we send a message. 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci long seqid; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* 2608c2ecf20Sopenharmony_ci * This is held so we can properly respond to the message on a 2618c2ecf20Sopenharmony_ci * timeout, and it is used to hold the temporary data for 2628c2ecf20Sopenharmony_ci * retransmission, too. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 2658c2ecf20Sopenharmony_ci}; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* 2688c2ecf20Sopenharmony_ci * Store the information in a msgid (long) to allow us to find a 2698c2ecf20Sopenharmony_ci * sequence table entry from the msgid. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci#define STORE_SEQ_IN_MSGID(seq, seqid) \ 2728c2ecf20Sopenharmony_ci ((((seq) & 0x3f) << 26) | ((seqid) & 0x3ffffff)) 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \ 2758c2ecf20Sopenharmony_ci do { \ 2768c2ecf20Sopenharmony_ci seq = (((msgid) >> 26) & 0x3f); \ 2778c2ecf20Sopenharmony_ci seqid = ((msgid) & 0x3ffffff); \ 2788c2ecf20Sopenharmony_ci } while (0) 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff) 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci#define IPMI_MAX_CHANNELS 16 2838c2ecf20Sopenharmony_cistruct ipmi_channel { 2848c2ecf20Sopenharmony_ci unsigned char medium; 2858c2ecf20Sopenharmony_ci unsigned char protocol; 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistruct ipmi_channel_set { 2898c2ecf20Sopenharmony_ci struct ipmi_channel c[IPMI_MAX_CHANNELS]; 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistruct ipmi_my_addrinfo { 2938c2ecf20Sopenharmony_ci /* 2948c2ecf20Sopenharmony_ci * My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, 2958c2ecf20Sopenharmony_ci * but may be changed by the user. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci unsigned char address; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* 3008c2ecf20Sopenharmony_ci * My LUN. This should generally stay the SMS LUN, but just in 3018c2ecf20Sopenharmony_ci * case... 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci unsigned char lun; 3048c2ecf20Sopenharmony_ci}; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* 3078c2ecf20Sopenharmony_ci * Note that the product id, manufacturer id, guid, and device id are 3088c2ecf20Sopenharmony_ci * immutable in this structure, so dyn_mutex is not required for 3098c2ecf20Sopenharmony_ci * accessing those. If those change on a BMC, a new BMC is allocated. 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistruct bmc_device { 3128c2ecf20Sopenharmony_ci struct platform_device pdev; 3138c2ecf20Sopenharmony_ci struct list_head intfs; /* Interfaces on this BMC. */ 3148c2ecf20Sopenharmony_ci struct ipmi_device_id id; 3158c2ecf20Sopenharmony_ci struct ipmi_device_id fetch_id; 3168c2ecf20Sopenharmony_ci int dyn_id_set; 3178c2ecf20Sopenharmony_ci unsigned long dyn_id_expiry; 3188c2ecf20Sopenharmony_ci struct mutex dyn_mutex; /* Protects id, intfs, & dyn* */ 3198c2ecf20Sopenharmony_ci guid_t guid; 3208c2ecf20Sopenharmony_ci guid_t fetch_guid; 3218c2ecf20Sopenharmony_ci int dyn_guid_set; 3228c2ecf20Sopenharmony_ci struct kref usecount; 3238c2ecf20Sopenharmony_ci struct work_struct remove_work; 3248c2ecf20Sopenharmony_ci unsigned char cc; /* completion code */ 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci#define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev) 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, 3298c2ecf20Sopenharmony_ci struct ipmi_device_id *id, 3308c2ecf20Sopenharmony_ci bool *guid_set, guid_t *guid); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* 3338c2ecf20Sopenharmony_ci * Various statistics for IPMI, these index stats[] in the ipmi_smi 3348c2ecf20Sopenharmony_ci * structure. 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_cienum ipmi_stat_indexes { 3378c2ecf20Sopenharmony_ci /* Commands we got from the user that were invalid. */ 3388c2ecf20Sopenharmony_ci IPMI_STAT_sent_invalid_commands = 0, 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Commands we sent to the MC. */ 3418c2ecf20Sopenharmony_ci IPMI_STAT_sent_local_commands, 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Responses from the MC that were delivered to a user. */ 3448c2ecf20Sopenharmony_ci IPMI_STAT_handled_local_responses, 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Responses from the MC that were not delivered to a user. */ 3478c2ecf20Sopenharmony_ci IPMI_STAT_unhandled_local_responses, 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Commands we sent out to the IPMB bus. */ 3508c2ecf20Sopenharmony_ci IPMI_STAT_sent_ipmb_commands, 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* Commands sent on the IPMB that had errors on the SEND CMD */ 3538c2ecf20Sopenharmony_ci IPMI_STAT_sent_ipmb_command_errs, 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Each retransmit increments this count. */ 3568c2ecf20Sopenharmony_ci IPMI_STAT_retransmitted_ipmb_commands, 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* 3598c2ecf20Sopenharmony_ci * When a message times out (runs out of retransmits) this is 3608c2ecf20Sopenharmony_ci * incremented. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci IPMI_STAT_timed_out_ipmb_commands, 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* 3658c2ecf20Sopenharmony_ci * This is like above, but for broadcasts. Broadcasts are 3668c2ecf20Sopenharmony_ci * *not* included in the above count (they are expected to 3678c2ecf20Sopenharmony_ci * time out). 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci IPMI_STAT_timed_out_ipmb_broadcasts, 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Responses I have sent to the IPMB bus. */ 3728c2ecf20Sopenharmony_ci IPMI_STAT_sent_ipmb_responses, 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* The response was delivered to the user. */ 3758c2ecf20Sopenharmony_ci IPMI_STAT_handled_ipmb_responses, 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* The response had invalid data in it. */ 3788c2ecf20Sopenharmony_ci IPMI_STAT_invalid_ipmb_responses, 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* The response didn't have anyone waiting for it. */ 3818c2ecf20Sopenharmony_ci IPMI_STAT_unhandled_ipmb_responses, 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Commands we sent out to the IPMB bus. */ 3848c2ecf20Sopenharmony_ci IPMI_STAT_sent_lan_commands, 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Commands sent on the IPMB that had errors on the SEND CMD */ 3878c2ecf20Sopenharmony_ci IPMI_STAT_sent_lan_command_errs, 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Each retransmit increments this count. */ 3908c2ecf20Sopenharmony_ci IPMI_STAT_retransmitted_lan_commands, 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* 3938c2ecf20Sopenharmony_ci * When a message times out (runs out of retransmits) this is 3948c2ecf20Sopenharmony_ci * incremented. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci IPMI_STAT_timed_out_lan_commands, 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Responses I have sent to the IPMB bus. */ 3998c2ecf20Sopenharmony_ci IPMI_STAT_sent_lan_responses, 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* The response was delivered to the user. */ 4028c2ecf20Sopenharmony_ci IPMI_STAT_handled_lan_responses, 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* The response had invalid data in it. */ 4058c2ecf20Sopenharmony_ci IPMI_STAT_invalid_lan_responses, 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* The response didn't have anyone waiting for it. */ 4088c2ecf20Sopenharmony_ci IPMI_STAT_unhandled_lan_responses, 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* The command was delivered to the user. */ 4118c2ecf20Sopenharmony_ci IPMI_STAT_handled_commands, 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* The command had invalid data in it. */ 4148c2ecf20Sopenharmony_ci IPMI_STAT_invalid_commands, 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* The command didn't have anyone waiting for it. */ 4178c2ecf20Sopenharmony_ci IPMI_STAT_unhandled_commands, 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Invalid data in an event. */ 4208c2ecf20Sopenharmony_ci IPMI_STAT_invalid_events, 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Events that were received with the proper format. */ 4238c2ecf20Sopenharmony_ci IPMI_STAT_events, 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Retransmissions on IPMB that failed. */ 4268c2ecf20Sopenharmony_ci IPMI_STAT_dropped_rexmit_ipmb_commands, 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Retransmissions on LAN that failed. */ 4298c2ecf20Sopenharmony_ci IPMI_STAT_dropped_rexmit_lan_commands, 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* This *must* remain last, add new values above this. */ 4328c2ecf20Sopenharmony_ci IPMI_NUM_STATS 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci#define IPMI_IPMB_NUM_SEQ 64 4378c2ecf20Sopenharmony_cistruct ipmi_smi { 4388c2ecf20Sopenharmony_ci struct module *owner; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* What interface number are we? */ 4418c2ecf20Sopenharmony_ci int intf_num; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci struct kref refcount; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* Set when the interface is being unregistered. */ 4468c2ecf20Sopenharmony_ci bool in_shutdown; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Used for a list of interfaces. */ 4498c2ecf20Sopenharmony_ci struct list_head link; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * The list of upper layers that are using me. seq_lock write 4538c2ecf20Sopenharmony_ci * protects this. Read protection is with srcu. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci struct list_head users; 4568c2ecf20Sopenharmony_ci struct srcu_struct users_srcu; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* Used for wake ups at startup. */ 4598c2ecf20Sopenharmony_ci wait_queue_head_t waitq; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* 4628c2ecf20Sopenharmony_ci * Prevents the interface from being unregistered when the 4638c2ecf20Sopenharmony_ci * interface is used by being looked up through the BMC 4648c2ecf20Sopenharmony_ci * structure. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci struct mutex bmc_reg_mutex; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci struct bmc_device tmp_bmc; 4698c2ecf20Sopenharmony_ci struct bmc_device *bmc; 4708c2ecf20Sopenharmony_ci bool bmc_registered; 4718c2ecf20Sopenharmony_ci struct list_head bmc_link; 4728c2ecf20Sopenharmony_ci char *my_dev_name; 4738c2ecf20Sopenharmony_ci bool in_bmc_register; /* Handle recursive situations. Yuck. */ 4748c2ecf20Sopenharmony_ci struct work_struct bmc_reg_work; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci const struct ipmi_smi_handlers *handlers; 4778c2ecf20Sopenharmony_ci void *send_info; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* Driver-model device for the system interface. */ 4808c2ecf20Sopenharmony_ci struct device *si_dev; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* 4838c2ecf20Sopenharmony_ci * A table of sequence numbers for this interface. We use the 4848c2ecf20Sopenharmony_ci * sequence numbers for IPMB messages that go out of the 4858c2ecf20Sopenharmony_ci * interface to match them up with their responses. A routine 4868c2ecf20Sopenharmony_ci * is called periodically to time the items in this list. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ci spinlock_t seq_lock; 4898c2ecf20Sopenharmony_ci struct seq_table seq_table[IPMI_IPMB_NUM_SEQ]; 4908c2ecf20Sopenharmony_ci int curr_seq; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* 4938c2ecf20Sopenharmony_ci * Messages queued for delivery. If delivery fails (out of memory 4948c2ecf20Sopenharmony_ci * for instance), They will stay in here to be processed later in a 4958c2ecf20Sopenharmony_ci * periodic timer interrupt. The tasklet is for handling received 4968c2ecf20Sopenharmony_ci * messages directly from the handler. 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_ci spinlock_t waiting_rcv_msgs_lock; 4998c2ecf20Sopenharmony_ci struct list_head waiting_rcv_msgs; 5008c2ecf20Sopenharmony_ci atomic_t watchdog_pretimeouts_to_deliver; 5018c2ecf20Sopenharmony_ci struct tasklet_struct recv_tasklet; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci spinlock_t xmit_msgs_lock; 5048c2ecf20Sopenharmony_ci struct list_head xmit_msgs; 5058c2ecf20Sopenharmony_ci struct ipmi_smi_msg *curr_msg; 5068c2ecf20Sopenharmony_ci struct list_head hp_xmit_msgs; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* 5098c2ecf20Sopenharmony_ci * The list of command receivers that are registered for commands 5108c2ecf20Sopenharmony_ci * on this interface. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci struct mutex cmd_rcvrs_mutex; 5138c2ecf20Sopenharmony_ci struct list_head cmd_rcvrs; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* 5168c2ecf20Sopenharmony_ci * Events that were queues because no one was there to receive 5178c2ecf20Sopenharmony_ci * them. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci spinlock_t events_lock; /* For dealing with event stuff. */ 5208c2ecf20Sopenharmony_ci struct list_head waiting_events; 5218c2ecf20Sopenharmony_ci unsigned int waiting_events_count; /* How many events in queue? */ 5228c2ecf20Sopenharmony_ci char delivering_events; 5238c2ecf20Sopenharmony_ci char event_msg_printed; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* How many users are waiting for events? */ 5268c2ecf20Sopenharmony_ci atomic_t event_waiters; 5278c2ecf20Sopenharmony_ci unsigned int ticks_to_req_ev; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci spinlock_t watch_lock; /* For dealing with watch stuff below. */ 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* How many users are waiting for commands? */ 5328c2ecf20Sopenharmony_ci unsigned int command_waiters; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* How many users are waiting for watchdogs? */ 5358c2ecf20Sopenharmony_ci unsigned int watchdog_waiters; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* How many users are waiting for message responses? */ 5388c2ecf20Sopenharmony_ci unsigned int response_waiters; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* 5418c2ecf20Sopenharmony_ci * Tells what the lower layer has last been asked to watch for, 5428c2ecf20Sopenharmony_ci * messages and/or watchdogs. Protected by watch_lock. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ci unsigned int last_watch_mask; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* 5478c2ecf20Sopenharmony_ci * The event receiver for my BMC, only really used at panic 5488c2ecf20Sopenharmony_ci * shutdown as a place to store this. 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_ci unsigned char event_receiver; 5518c2ecf20Sopenharmony_ci unsigned char event_receiver_lun; 5528c2ecf20Sopenharmony_ci unsigned char local_sel_device; 5538c2ecf20Sopenharmony_ci unsigned char local_event_generator; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* For handling of maintenance mode. */ 5568c2ecf20Sopenharmony_ci int maintenance_mode; 5578c2ecf20Sopenharmony_ci bool maintenance_mode_enable; 5588c2ecf20Sopenharmony_ci int auto_maintenance_timeout; 5598c2ecf20Sopenharmony_ci spinlock_t maintenance_mode_lock; /* Used in a timer... */ 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * If we are doing maintenance on something on IPMB, extend 5638c2ecf20Sopenharmony_ci * the timeout time to avoid timeouts writing firmware and 5648c2ecf20Sopenharmony_ci * such. 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci int ipmb_maintenance_mode_timeout; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * A cheap hack, if this is non-null and a message to an 5708c2ecf20Sopenharmony_ci * interface comes in with a NULL user, call this routine with 5718c2ecf20Sopenharmony_ci * it. Note that the message will still be freed by the 5728c2ecf20Sopenharmony_ci * caller. This only works on the system interface. 5738c2ecf20Sopenharmony_ci * 5748c2ecf20Sopenharmony_ci * Protected by bmc_reg_mutex. 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci void (*null_user_handler)(struct ipmi_smi *intf, 5778c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* 5808c2ecf20Sopenharmony_ci * When we are scanning the channels for an SMI, this will 5818c2ecf20Sopenharmony_ci * tell which channel we are scanning. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci int curr_channel; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Channel information */ 5868c2ecf20Sopenharmony_ci struct ipmi_channel_set *channel_list; 5878c2ecf20Sopenharmony_ci unsigned int curr_working_cset; /* First index into the following. */ 5888c2ecf20Sopenharmony_ci struct ipmi_channel_set wchannels[2]; 5898c2ecf20Sopenharmony_ci struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS]; 5908c2ecf20Sopenharmony_ci bool channels_ready; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci atomic_t stats[IPMI_NUM_STATS]; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* 5958c2ecf20Sopenharmony_ci * run_to_completion duplicate of smb_info, smi_info 5968c2ecf20Sopenharmony_ci * and ipmi_serial_info structures. Used to decrease numbers of 5978c2ecf20Sopenharmony_ci * parameters passed by "low" level IPMI code. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci int run_to_completion; 6008c2ecf20Sopenharmony_ci}; 6018c2ecf20Sopenharmony_ci#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic void __get_guid(struct ipmi_smi *intf); 6048c2ecf20Sopenharmony_cistatic void __ipmi_bmc_unregister(struct ipmi_smi *intf); 6058c2ecf20Sopenharmony_cistatic int __ipmi_bmc_register(struct ipmi_smi *intf, 6068c2ecf20Sopenharmony_ci struct ipmi_device_id *id, 6078c2ecf20Sopenharmony_ci bool guid_set, guid_t *guid, int intf_num); 6088c2ecf20Sopenharmony_cistatic int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci/** 6128c2ecf20Sopenharmony_ci * The driver model view of the IPMI messaging driver. 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_cistatic struct platform_driver ipmidriver = { 6158c2ecf20Sopenharmony_ci .driver = { 6168c2ecf20Sopenharmony_ci .name = "ipmi", 6178c2ecf20Sopenharmony_ci .bus = &platform_bus_type 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci}; 6208c2ecf20Sopenharmony_ci/* 6218c2ecf20Sopenharmony_ci * This mutex keeps us from adding the same BMC twice. 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ipmidriver_mutex); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic LIST_HEAD(ipmi_interfaces); 6268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ipmi_interfaces_mutex); 6278c2ecf20Sopenharmony_ci#define ipmi_interfaces_mutex_held() \ 6288c2ecf20Sopenharmony_ci lockdep_is_held(&ipmi_interfaces_mutex) 6298c2ecf20Sopenharmony_cistatic struct srcu_struct ipmi_interfaces_srcu; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci/* 6328c2ecf20Sopenharmony_ci * List of watchers that want to know when smi's are added and deleted. 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_cistatic LIST_HEAD(smi_watchers); 6358c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(smi_watchers_mutex); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci#define ipmi_inc_stat(intf, stat) \ 6388c2ecf20Sopenharmony_ci atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat]) 6398c2ecf20Sopenharmony_ci#define ipmi_get_stat(intf, stat) \ 6408c2ecf20Sopenharmony_ci ((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat])) 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic const char * const addr_src_to_str[] = { 6438c2ecf20Sopenharmony_ci "invalid", "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI", 6448c2ecf20Sopenharmony_ci "device-tree", "platform" 6458c2ecf20Sopenharmony_ci}; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ciconst char *ipmi_addr_src_to_str(enum ipmi_addr_src src) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci if (src >= SI_LAST) 6508c2ecf20Sopenharmony_ci src = 0; /* Invalid */ 6518c2ecf20Sopenharmony_ci return addr_src_to_str[src]; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_addr_src_to_str); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int is_lan_addr(struct ipmi_addr *addr) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci return addr->addr_type == IPMI_LAN_ADDR_TYPE; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int is_ipmb_addr(struct ipmi_addr *addr) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci return addr->addr_type == IPMI_IPMB_ADDR_TYPE; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic int is_ipmb_bcast_addr(struct ipmi_addr *addr) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic void free_recv_msg_list(struct list_head *q) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg, *msg2; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg, msg2, q, link) { 6758c2ecf20Sopenharmony_ci list_del(&msg->link); 6768c2ecf20Sopenharmony_ci ipmi_free_recv_msg(msg); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic void free_smi_msg_list(struct list_head *q) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg, *msg2; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg, msg2, q, link) { 6858c2ecf20Sopenharmony_ci list_del(&msg->link); 6868c2ecf20Sopenharmony_ci ipmi_free_smi_msg(msg); 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic void clean_up_interface_data(struct ipmi_smi *intf) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci int i; 6938c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr, *rcvr2; 6948c2ecf20Sopenharmony_ci struct list_head list; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci tasklet_kill(&intf->recv_tasklet); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci free_smi_msg_list(&intf->waiting_rcv_msgs); 6998c2ecf20Sopenharmony_ci free_recv_msg_list(&intf->waiting_events); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* 7028c2ecf20Sopenharmony_ci * Wholesale remove all the entries from the list in the 7038c2ecf20Sopenharmony_ci * interface and wait for RCU to know that none are in use. 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_ci mutex_lock(&intf->cmd_rcvrs_mutex); 7068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list); 7078c2ecf20Sopenharmony_ci list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu); 7088c2ecf20Sopenharmony_ci mutex_unlock(&intf->cmd_rcvrs_mutex); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci list_for_each_entry_safe(rcvr, rcvr2, &list, link) 7118c2ecf20Sopenharmony_ci kfree(rcvr); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { 7148c2ecf20Sopenharmony_ci if ((intf->seq_table[i].inuse) 7158c2ecf20Sopenharmony_ci && (intf->seq_table[i].recv_msg)) 7168c2ecf20Sopenharmony_ci ipmi_free_recv_msg(intf->seq_table[i].recv_msg); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic void intf_free(struct kref *ref) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci clean_up_interface_data(intf); 7258c2ecf20Sopenharmony_ci kfree(intf); 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistruct watcher_entry { 7298c2ecf20Sopenharmony_ci int intf_num; 7308c2ecf20Sopenharmony_ci struct ipmi_smi *intf; 7318c2ecf20Sopenharmony_ci struct list_head link; 7328c2ecf20Sopenharmony_ci}; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ciint ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct ipmi_smi *intf; 7378c2ecf20Sopenharmony_ci int index, rv; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* 7408c2ecf20Sopenharmony_ci * Make sure the driver is actually initialized, this handles 7418c2ecf20Sopenharmony_ci * problems with initialization order. 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci rv = ipmi_init_msghandler(); 7448c2ecf20Sopenharmony_ci if (rv) 7458c2ecf20Sopenharmony_ci return rv; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci mutex_lock(&smi_watchers_mutex); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci list_add(&watcher->link, &smi_watchers); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci index = srcu_read_lock(&ipmi_interfaces_srcu); 7528c2ecf20Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link, 7538c2ecf20Sopenharmony_ci lockdep_is_held(&smi_watchers_mutex)) { 7548c2ecf20Sopenharmony_ci int intf_num = READ_ONCE(intf->intf_num); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (intf_num == -1) 7578c2ecf20Sopenharmony_ci continue; 7588c2ecf20Sopenharmony_ci watcher->new_smi(intf_num, intf->si_dev); 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci mutex_unlock(&smi_watchers_mutex); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watcher_register); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ciint ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci mutex_lock(&smi_watchers_mutex); 7718c2ecf20Sopenharmony_ci list_del(&watcher->link); 7728c2ecf20Sopenharmony_ci mutex_unlock(&smi_watchers_mutex); 7738c2ecf20Sopenharmony_ci return 0; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watcher_unregister); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci/* 7788c2ecf20Sopenharmony_ci * Must be called with smi_watchers_mutex held. 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_cistatic void 7818c2ecf20Sopenharmony_cicall_smi_watchers(int i, struct device *dev) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct ipmi_smi_watcher *w; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci mutex_lock(&smi_watchers_mutex); 7868c2ecf20Sopenharmony_ci list_for_each_entry(w, &smi_watchers, link) { 7878c2ecf20Sopenharmony_ci if (try_module_get(w->owner)) { 7888c2ecf20Sopenharmony_ci w->new_smi(i, dev); 7898c2ecf20Sopenharmony_ci module_put(w->owner); 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci mutex_unlock(&smi_watchers_mutex); 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic int 7968c2ecf20Sopenharmony_ciipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci if (addr1->addr_type != addr2->addr_type) 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (addr1->channel != addr2->channel) 8028c2ecf20Sopenharmony_ci return 0; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { 8058c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr1 8068c2ecf20Sopenharmony_ci = (struct ipmi_system_interface_addr *) addr1; 8078c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr2 8088c2ecf20Sopenharmony_ci = (struct ipmi_system_interface_addr *) addr2; 8098c2ecf20Sopenharmony_ci return (smi_addr1->lun == smi_addr2->lun); 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (is_ipmb_addr(addr1) || is_ipmb_bcast_addr(addr1)) { 8138c2ecf20Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr1 8148c2ecf20Sopenharmony_ci = (struct ipmi_ipmb_addr *) addr1; 8158c2ecf20Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr2 8168c2ecf20Sopenharmony_ci = (struct ipmi_ipmb_addr *) addr2; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr) 8198c2ecf20Sopenharmony_ci && (ipmb_addr1->lun == ipmb_addr2->lun)); 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (is_lan_addr(addr1)) { 8238c2ecf20Sopenharmony_ci struct ipmi_lan_addr *lan_addr1 8248c2ecf20Sopenharmony_ci = (struct ipmi_lan_addr *) addr1; 8258c2ecf20Sopenharmony_ci struct ipmi_lan_addr *lan_addr2 8268c2ecf20Sopenharmony_ci = (struct ipmi_lan_addr *) addr2; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci return ((lan_addr1->remote_SWID == lan_addr2->remote_SWID) 8298c2ecf20Sopenharmony_ci && (lan_addr1->local_SWID == lan_addr2->local_SWID) 8308c2ecf20Sopenharmony_ci && (lan_addr1->session_handle 8318c2ecf20Sopenharmony_ci == lan_addr2->session_handle) 8328c2ecf20Sopenharmony_ci && (lan_addr1->lun == lan_addr2->lun)); 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci return 1; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ciint ipmi_validate_addr(struct ipmi_addr *addr, int len) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci if (len < sizeof(struct ipmi_system_interface_addr)) 8418c2ecf20Sopenharmony_ci return -EINVAL; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { 8448c2ecf20Sopenharmony_ci if (addr->channel != IPMI_BMC_CHANNEL) 8458c2ecf20Sopenharmony_ci return -EINVAL; 8468c2ecf20Sopenharmony_ci return 0; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if ((addr->channel == IPMI_BMC_CHANNEL) 8508c2ecf20Sopenharmony_ci || (addr->channel >= IPMI_MAX_CHANNELS) 8518c2ecf20Sopenharmony_ci || (addr->channel < 0)) 8528c2ecf20Sopenharmony_ci return -EINVAL; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { 8558c2ecf20Sopenharmony_ci if (len < sizeof(struct ipmi_ipmb_addr)) 8568c2ecf20Sopenharmony_ci return -EINVAL; 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (is_lan_addr(addr)) { 8618c2ecf20Sopenharmony_ci if (len < sizeof(struct ipmi_lan_addr)) 8628c2ecf20Sopenharmony_ci return -EINVAL; 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci return -EINVAL; 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_validate_addr); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ciunsigned int ipmi_addr_length(int addr_type) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 8738c2ecf20Sopenharmony_ci return sizeof(struct ipmi_system_interface_addr); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if ((addr_type == IPMI_IPMB_ADDR_TYPE) 8768c2ecf20Sopenharmony_ci || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) 8778c2ecf20Sopenharmony_ci return sizeof(struct ipmi_ipmb_addr); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (addr_type == IPMI_LAN_ADDR_TYPE) 8808c2ecf20Sopenharmony_ci return sizeof(struct ipmi_lan_addr); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci return 0; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_addr_length); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci int rv = 0; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (!msg->user) { 8918c2ecf20Sopenharmony_ci /* Special handling for NULL users. */ 8928c2ecf20Sopenharmony_ci if (intf->null_user_handler) { 8938c2ecf20Sopenharmony_ci intf->null_user_handler(intf, msg); 8948c2ecf20Sopenharmony_ci } else { 8958c2ecf20Sopenharmony_ci /* No handler, so give up. */ 8968c2ecf20Sopenharmony_ci rv = -EINVAL; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci ipmi_free_recv_msg(msg); 8998c2ecf20Sopenharmony_ci } else if (oops_in_progress) { 9008c2ecf20Sopenharmony_ci /* 9018c2ecf20Sopenharmony_ci * If we are running in the panic context, calling the 9028c2ecf20Sopenharmony_ci * receive handler doesn't much meaning and has a deadlock 9038c2ecf20Sopenharmony_ci * risk. At this moment, simply skip it in that case. 9048c2ecf20Sopenharmony_ci */ 9058c2ecf20Sopenharmony_ci ipmi_free_recv_msg(msg); 9068c2ecf20Sopenharmony_ci } else { 9078c2ecf20Sopenharmony_ci int index; 9088c2ecf20Sopenharmony_ci struct ipmi_user *user = acquire_ipmi_user(msg->user, &index); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (user) { 9118c2ecf20Sopenharmony_ci user->handler->ipmi_recv_hndl(msg, user->handler_data); 9128c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 9138c2ecf20Sopenharmony_ci } else { 9148c2ecf20Sopenharmony_ci /* User went away, give up. */ 9158c2ecf20Sopenharmony_ci ipmi_free_recv_msg(msg); 9168c2ecf20Sopenharmony_ci rv = -EINVAL; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci return rv; 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic void deliver_local_response(struct ipmi_smi *intf, 9248c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci if (deliver_response(intf, msg)) 9278c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_local_responses); 9288c2ecf20Sopenharmony_ci else 9298c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, handled_local_responses); 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic void deliver_err_response(struct ipmi_smi *intf, 9338c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg, int err) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 9368c2ecf20Sopenharmony_ci msg->msg_data[0] = err; 9378c2ecf20Sopenharmony_ci msg->msg.netfn |= 1; /* Convert to a response. */ 9388c2ecf20Sopenharmony_ci msg->msg.data_len = 1; 9398c2ecf20Sopenharmony_ci msg->msg.data = msg->msg_data; 9408c2ecf20Sopenharmony_ci deliver_local_response(intf, msg); 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic void smi_add_watch(struct ipmi_smi *intf, unsigned int flags) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci unsigned long iflags; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (!intf->handlers->set_need_watch) 9488c2ecf20Sopenharmony_ci return; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->watch_lock, iflags); 9518c2ecf20Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES) 9528c2ecf20Sopenharmony_ci intf->response_waiters++; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG) 9558c2ecf20Sopenharmony_ci intf->watchdog_waiters++; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS) 9588c2ecf20Sopenharmony_ci intf->command_waiters++; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if ((intf->last_watch_mask & flags) != flags) { 9618c2ecf20Sopenharmony_ci intf->last_watch_mask |= flags; 9628c2ecf20Sopenharmony_ci intf->handlers->set_need_watch(intf->send_info, 9638c2ecf20Sopenharmony_ci intf->last_watch_mask); 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->watch_lock, iflags); 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic void smi_remove_watch(struct ipmi_smi *intf, unsigned int flags) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci unsigned long iflags; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (!intf->handlers->set_need_watch) 9738c2ecf20Sopenharmony_ci return; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->watch_lock, iflags); 9768c2ecf20Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES) 9778c2ecf20Sopenharmony_ci intf->response_waiters--; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG) 9808c2ecf20Sopenharmony_ci intf->watchdog_waiters--; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS) 9838c2ecf20Sopenharmony_ci intf->command_waiters--; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci flags = 0; 9868c2ecf20Sopenharmony_ci if (intf->response_waiters) 9878c2ecf20Sopenharmony_ci flags |= IPMI_WATCH_MASK_CHECK_MESSAGES; 9888c2ecf20Sopenharmony_ci if (intf->watchdog_waiters) 9898c2ecf20Sopenharmony_ci flags |= IPMI_WATCH_MASK_CHECK_WATCHDOG; 9908c2ecf20Sopenharmony_ci if (intf->command_waiters) 9918c2ecf20Sopenharmony_ci flags |= IPMI_WATCH_MASK_CHECK_COMMANDS; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (intf->last_watch_mask != flags) { 9948c2ecf20Sopenharmony_ci intf->last_watch_mask = flags; 9958c2ecf20Sopenharmony_ci intf->handlers->set_need_watch(intf->send_info, 9968c2ecf20Sopenharmony_ci intf->last_watch_mask); 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->watch_lock, iflags); 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci/* 10028c2ecf20Sopenharmony_ci * Find the next sequence number not being used and add the given 10038c2ecf20Sopenharmony_ci * message with the given timeout to the sequence table. This must be 10048c2ecf20Sopenharmony_ci * called with the interface's seq_lock held. 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_cistatic int intf_next_seq(struct ipmi_smi *intf, 10078c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 10088c2ecf20Sopenharmony_ci unsigned long timeout, 10098c2ecf20Sopenharmony_ci int retries, 10108c2ecf20Sopenharmony_ci int broadcast, 10118c2ecf20Sopenharmony_ci unsigned char *seq, 10128c2ecf20Sopenharmony_ci long *seqid) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci int rv = 0; 10158c2ecf20Sopenharmony_ci unsigned int i; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (timeout == 0) 10188c2ecf20Sopenharmony_ci timeout = default_retry_ms; 10198c2ecf20Sopenharmony_ci if (retries < 0) 10208c2ecf20Sopenharmony_ci retries = default_max_retries; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; 10238c2ecf20Sopenharmony_ci i = (i+1)%IPMI_IPMB_NUM_SEQ) { 10248c2ecf20Sopenharmony_ci if (!intf->seq_table[i].inuse) 10258c2ecf20Sopenharmony_ci break; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (!intf->seq_table[i].inuse) { 10298c2ecf20Sopenharmony_ci intf->seq_table[i].recv_msg = recv_msg; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* 10328c2ecf20Sopenharmony_ci * Start with the maximum timeout, when the send response 10338c2ecf20Sopenharmony_ci * comes in we will start the real timer. 10348c2ecf20Sopenharmony_ci */ 10358c2ecf20Sopenharmony_ci intf->seq_table[i].timeout = MAX_MSG_TIMEOUT; 10368c2ecf20Sopenharmony_ci intf->seq_table[i].orig_timeout = timeout; 10378c2ecf20Sopenharmony_ci intf->seq_table[i].retries_left = retries; 10388c2ecf20Sopenharmony_ci intf->seq_table[i].broadcast = broadcast; 10398c2ecf20Sopenharmony_ci intf->seq_table[i].inuse = 1; 10408c2ecf20Sopenharmony_ci intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid); 10418c2ecf20Sopenharmony_ci *seq = i; 10428c2ecf20Sopenharmony_ci *seqid = intf->seq_table[i].seqid; 10438c2ecf20Sopenharmony_ci intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ; 10448c2ecf20Sopenharmony_ci smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 10458c2ecf20Sopenharmony_ci need_waiter(intf); 10468c2ecf20Sopenharmony_ci } else { 10478c2ecf20Sopenharmony_ci rv = -EAGAIN; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci return rv; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci/* 10548c2ecf20Sopenharmony_ci * Return the receive message for the given sequence number and 10558c2ecf20Sopenharmony_ci * release the sequence number so it can be reused. Some other data 10568c2ecf20Sopenharmony_ci * is passed in to be sure the message matches up correctly (to help 10578c2ecf20Sopenharmony_ci * guard against message coming in after their timeout and the 10588c2ecf20Sopenharmony_ci * sequence number being reused). 10598c2ecf20Sopenharmony_ci */ 10608c2ecf20Sopenharmony_cistatic int intf_find_seq(struct ipmi_smi *intf, 10618c2ecf20Sopenharmony_ci unsigned char seq, 10628c2ecf20Sopenharmony_ci short channel, 10638c2ecf20Sopenharmony_ci unsigned char cmd, 10648c2ecf20Sopenharmony_ci unsigned char netfn, 10658c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 10668c2ecf20Sopenharmony_ci struct ipmi_recv_msg **recv_msg) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci int rv = -ENODEV; 10698c2ecf20Sopenharmony_ci unsigned long flags; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci if (seq >= IPMI_IPMB_NUM_SEQ) 10728c2ecf20Sopenharmony_ci return -EINVAL; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 10758c2ecf20Sopenharmony_ci if (intf->seq_table[seq].inuse) { 10768c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd) 10798c2ecf20Sopenharmony_ci && (msg->msg.netfn == netfn) 10808c2ecf20Sopenharmony_ci && (ipmi_addr_equal(addr, &msg->addr))) { 10818c2ecf20Sopenharmony_ci *recv_msg = msg; 10828c2ecf20Sopenharmony_ci intf->seq_table[seq].inuse = 0; 10838c2ecf20Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 10848c2ecf20Sopenharmony_ci rv = 0; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci return rv; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci/* Start the timer for a specific sequence table entry. */ 10948c2ecf20Sopenharmony_cistatic int intf_start_seq_timer(struct ipmi_smi *intf, 10958c2ecf20Sopenharmony_ci long msgid) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci int rv = -ENODEV; 10988c2ecf20Sopenharmony_ci unsigned long flags; 10998c2ecf20Sopenharmony_ci unsigned char seq; 11008c2ecf20Sopenharmony_ci unsigned long seqid; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci GET_SEQ_FROM_MSGID(msgid, seq, seqid); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 11068c2ecf20Sopenharmony_ci /* 11078c2ecf20Sopenharmony_ci * We do this verification because the user can be deleted 11088c2ecf20Sopenharmony_ci * while a message is outstanding. 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_ci if ((intf->seq_table[seq].inuse) 11118c2ecf20Sopenharmony_ci && (intf->seq_table[seq].seqid == seqid)) { 11128c2ecf20Sopenharmony_ci struct seq_table *ent = &intf->seq_table[seq]; 11138c2ecf20Sopenharmony_ci ent->timeout = ent->orig_timeout; 11148c2ecf20Sopenharmony_ci rv = 0; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci return rv; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci/* Got an error for the send message for a specific sequence number. */ 11228c2ecf20Sopenharmony_cistatic int intf_err_seq(struct ipmi_smi *intf, 11238c2ecf20Sopenharmony_ci long msgid, 11248c2ecf20Sopenharmony_ci unsigned int err) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci int rv = -ENODEV; 11278c2ecf20Sopenharmony_ci unsigned long flags; 11288c2ecf20Sopenharmony_ci unsigned char seq; 11298c2ecf20Sopenharmony_ci unsigned long seqid; 11308c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg = NULL; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci GET_SEQ_FROM_MSGID(msgid, seq, seqid); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 11368c2ecf20Sopenharmony_ci /* 11378c2ecf20Sopenharmony_ci * We do this verification because the user can be deleted 11388c2ecf20Sopenharmony_ci * while a message is outstanding. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ci if ((intf->seq_table[seq].inuse) 11418c2ecf20Sopenharmony_ci && (intf->seq_table[seq].seqid == seqid)) { 11428c2ecf20Sopenharmony_ci struct seq_table *ent = &intf->seq_table[seq]; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci ent->inuse = 0; 11458c2ecf20Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 11468c2ecf20Sopenharmony_ci msg = ent->recv_msg; 11478c2ecf20Sopenharmony_ci rv = 0; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci if (msg) 11528c2ecf20Sopenharmony_ci deliver_err_response(intf, msg, err); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci return rv; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic void free_user_work(struct work_struct *work) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct ipmi_user *user = container_of(work, struct ipmi_user, 11608c2ecf20Sopenharmony_ci remove_work); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci cleanup_srcu_struct(&user->release_barrier); 11638c2ecf20Sopenharmony_ci vfree(user); 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ciint ipmi_create_user(unsigned int if_num, 11678c2ecf20Sopenharmony_ci const struct ipmi_user_hndl *handler, 11688c2ecf20Sopenharmony_ci void *handler_data, 11698c2ecf20Sopenharmony_ci struct ipmi_user **user) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci unsigned long flags; 11728c2ecf20Sopenharmony_ci struct ipmi_user *new_user; 11738c2ecf20Sopenharmony_ci int rv, index; 11748c2ecf20Sopenharmony_ci struct ipmi_smi *intf; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci /* 11778c2ecf20Sopenharmony_ci * There is no module usecount here, because it's not 11788c2ecf20Sopenharmony_ci * required. Since this can only be used by and called from 11798c2ecf20Sopenharmony_ci * other modules, they will implicitly use this module, and 11808c2ecf20Sopenharmony_ci * thus this can't be removed unless the other modules are 11818c2ecf20Sopenharmony_ci * removed. 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (handler == NULL) 11858c2ecf20Sopenharmony_ci return -EINVAL; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci /* 11888c2ecf20Sopenharmony_ci * Make sure the driver is actually initialized, this handles 11898c2ecf20Sopenharmony_ci * problems with initialization order. 11908c2ecf20Sopenharmony_ci */ 11918c2ecf20Sopenharmony_ci rv = ipmi_init_msghandler(); 11928c2ecf20Sopenharmony_ci if (rv) 11938c2ecf20Sopenharmony_ci return rv; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci new_user = vzalloc(sizeof(*new_user)); 11968c2ecf20Sopenharmony_ci if (!new_user) 11978c2ecf20Sopenharmony_ci return -ENOMEM; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci index = srcu_read_lock(&ipmi_interfaces_srcu); 12008c2ecf20Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 12018c2ecf20Sopenharmony_ci if (intf->intf_num == if_num) 12028c2ecf20Sopenharmony_ci goto found; 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci /* Not found, return an error */ 12058c2ecf20Sopenharmony_ci rv = -EINVAL; 12068c2ecf20Sopenharmony_ci goto out_kfree; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci found: 12098c2ecf20Sopenharmony_ci INIT_WORK(&new_user->remove_work, free_user_work); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci rv = init_srcu_struct(&new_user->release_barrier); 12128c2ecf20Sopenharmony_ci if (rv) 12138c2ecf20Sopenharmony_ci goto out_kfree; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (!try_module_get(intf->owner)) { 12168c2ecf20Sopenharmony_ci rv = -ENODEV; 12178c2ecf20Sopenharmony_ci goto out_kfree; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* Note that each existing user holds a refcount to the interface. */ 12218c2ecf20Sopenharmony_ci kref_get(&intf->refcount); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci kref_init(&new_user->refcount); 12248c2ecf20Sopenharmony_ci new_user->handler = handler; 12258c2ecf20Sopenharmony_ci new_user->handler_data = handler_data; 12268c2ecf20Sopenharmony_ci new_user->intf = intf; 12278c2ecf20Sopenharmony_ci new_user->gets_events = false; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci rcu_assign_pointer(new_user->self, new_user); 12308c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 12318c2ecf20Sopenharmony_ci list_add_rcu(&new_user->link, &intf->users); 12328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 12338c2ecf20Sopenharmony_ci if (handler->ipmi_watchdog_pretimeout) 12348c2ecf20Sopenharmony_ci /* User wants pretimeouts, so make sure to watch for them. */ 12358c2ecf20Sopenharmony_ci smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG); 12368c2ecf20Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 12378c2ecf20Sopenharmony_ci *user = new_user; 12388c2ecf20Sopenharmony_ci return 0; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ciout_kfree: 12418c2ecf20Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 12428c2ecf20Sopenharmony_ci vfree(new_user); 12438c2ecf20Sopenharmony_ci return rv; 12448c2ecf20Sopenharmony_ci} 12458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_create_user); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ciint ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci int rv, index; 12508c2ecf20Sopenharmony_ci struct ipmi_smi *intf; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci index = srcu_read_lock(&ipmi_interfaces_srcu); 12538c2ecf20Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 12548c2ecf20Sopenharmony_ci if (intf->intf_num == if_num) 12558c2ecf20Sopenharmony_ci goto found; 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* Not found, return an error */ 12608c2ecf20Sopenharmony_ci return -EINVAL; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cifound: 12638c2ecf20Sopenharmony_ci if (!intf->handlers->get_smi_info) 12648c2ecf20Sopenharmony_ci rv = -ENOTTY; 12658c2ecf20Sopenharmony_ci else 12668c2ecf20Sopenharmony_ci rv = intf->handlers->get_smi_info(intf->send_info, data); 12678c2ecf20Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci return rv; 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_smi_info); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic void free_user(struct kref *ref) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* SRCU cleanup must happen in task context. */ 12788c2ecf20Sopenharmony_ci queue_work(remove_work_wq, &user->remove_work); 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic void _ipmi_destroy_user(struct ipmi_user *user) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci struct ipmi_smi *intf = user->intf; 12848c2ecf20Sopenharmony_ci int i; 12858c2ecf20Sopenharmony_ci unsigned long flags; 12868c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr; 12878c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvrs = NULL; 12888c2ecf20Sopenharmony_ci struct module *owner; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci if (!acquire_ipmi_user(user, &i)) { 12918c2ecf20Sopenharmony_ci /* 12928c2ecf20Sopenharmony_ci * The user has already been cleaned up, just make sure 12938c2ecf20Sopenharmony_ci * nothing is using it and return. 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_ci synchronize_srcu(&user->release_barrier); 12968c2ecf20Sopenharmony_ci return; 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci rcu_assign_pointer(user->self, NULL); 13008c2ecf20Sopenharmony_ci release_ipmi_user(user, i); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci synchronize_srcu(&user->release_barrier); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (user->handler->shutdown) 13058c2ecf20Sopenharmony_ci user->handler->shutdown(user->handler_data); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci if (user->handler->ipmi_watchdog_pretimeout) 13088c2ecf20Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (user->gets_events) 13118c2ecf20Sopenharmony_ci atomic_dec(&intf->event_waiters); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci /* Remove the user from the interface's sequence table. */ 13148c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 13158c2ecf20Sopenharmony_ci list_del_rcu(&user->link); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { 13188c2ecf20Sopenharmony_ci if (intf->seq_table[i].inuse 13198c2ecf20Sopenharmony_ci && (intf->seq_table[i].recv_msg->user == user)) { 13208c2ecf20Sopenharmony_ci intf->seq_table[i].inuse = 0; 13218c2ecf20Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 13228c2ecf20Sopenharmony_ci ipmi_free_recv_msg(intf->seq_table[i].recv_msg); 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* 13288c2ecf20Sopenharmony_ci * Remove the user from the command receiver's table. First 13298c2ecf20Sopenharmony_ci * we build a list of everything (not using the standard link, 13308c2ecf20Sopenharmony_ci * since other things may be using it till we do 13318c2ecf20Sopenharmony_ci * synchronize_srcu()) then free everything in that list. 13328c2ecf20Sopenharmony_ci */ 13338c2ecf20Sopenharmony_ci mutex_lock(&intf->cmd_rcvrs_mutex); 13348c2ecf20Sopenharmony_ci list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link, 13358c2ecf20Sopenharmony_ci lockdep_is_held(&intf->cmd_rcvrs_mutex)) { 13368c2ecf20Sopenharmony_ci if (rcvr->user == user) { 13378c2ecf20Sopenharmony_ci list_del_rcu(&rcvr->link); 13388c2ecf20Sopenharmony_ci rcvr->next = rcvrs; 13398c2ecf20Sopenharmony_ci rcvrs = rcvr; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci mutex_unlock(&intf->cmd_rcvrs_mutex); 13438c2ecf20Sopenharmony_ci synchronize_rcu(); 13448c2ecf20Sopenharmony_ci while (rcvrs) { 13458c2ecf20Sopenharmony_ci rcvr = rcvrs; 13468c2ecf20Sopenharmony_ci rcvrs = rcvr->next; 13478c2ecf20Sopenharmony_ci kfree(rcvr); 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci owner = intf->owner; 13518c2ecf20Sopenharmony_ci kref_put(&intf->refcount, intf_free); 13528c2ecf20Sopenharmony_ci module_put(owner); 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ciint ipmi_destroy_user(struct ipmi_user *user) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci _ipmi_destroy_user(user); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci kref_put(&user->refcount, free_user); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci return 0; 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_destroy_user); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ciint ipmi_get_version(struct ipmi_user *user, 13668c2ecf20Sopenharmony_ci unsigned char *major, 13678c2ecf20Sopenharmony_ci unsigned char *minor) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci struct ipmi_device_id id; 13708c2ecf20Sopenharmony_ci int rv, index; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 13738c2ecf20Sopenharmony_ci if (!user) 13748c2ecf20Sopenharmony_ci return -ENODEV; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL); 13778c2ecf20Sopenharmony_ci if (!rv) { 13788c2ecf20Sopenharmony_ci *major = ipmi_version_major(&id); 13798c2ecf20Sopenharmony_ci *minor = ipmi_version_minor(&id); 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci return rv; 13848c2ecf20Sopenharmony_ci} 13858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_version); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ciint ipmi_set_my_address(struct ipmi_user *user, 13888c2ecf20Sopenharmony_ci unsigned int channel, 13898c2ecf20Sopenharmony_ci unsigned char address) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci int index, rv = 0; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 13948c2ecf20Sopenharmony_ci if (!user) 13958c2ecf20Sopenharmony_ci return -ENODEV; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (channel >= IPMI_MAX_CHANNELS) { 13988c2ecf20Sopenharmony_ci rv = -EINVAL; 13998c2ecf20Sopenharmony_ci } else { 14008c2ecf20Sopenharmony_ci channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); 14018c2ecf20Sopenharmony_ci user->intf->addrinfo[channel].address = address; 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci return rv; 14068c2ecf20Sopenharmony_ci} 14078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_my_address); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ciint ipmi_get_my_address(struct ipmi_user *user, 14108c2ecf20Sopenharmony_ci unsigned int channel, 14118c2ecf20Sopenharmony_ci unsigned char *address) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci int index, rv = 0; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 14168c2ecf20Sopenharmony_ci if (!user) 14178c2ecf20Sopenharmony_ci return -ENODEV; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci if (channel >= IPMI_MAX_CHANNELS) { 14208c2ecf20Sopenharmony_ci rv = -EINVAL; 14218c2ecf20Sopenharmony_ci } else { 14228c2ecf20Sopenharmony_ci channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); 14238c2ecf20Sopenharmony_ci *address = user->intf->addrinfo[channel].address; 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci return rv; 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_my_address); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ciint ipmi_set_my_LUN(struct ipmi_user *user, 14328c2ecf20Sopenharmony_ci unsigned int channel, 14338c2ecf20Sopenharmony_ci unsigned char LUN) 14348c2ecf20Sopenharmony_ci{ 14358c2ecf20Sopenharmony_ci int index, rv = 0; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 14388c2ecf20Sopenharmony_ci if (!user) 14398c2ecf20Sopenharmony_ci return -ENODEV; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci if (channel >= IPMI_MAX_CHANNELS) { 14428c2ecf20Sopenharmony_ci rv = -EINVAL; 14438c2ecf20Sopenharmony_ci } else { 14448c2ecf20Sopenharmony_ci channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); 14458c2ecf20Sopenharmony_ci user->intf->addrinfo[channel].lun = LUN & 0x3; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci return rv; 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_my_LUN); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ciint ipmi_get_my_LUN(struct ipmi_user *user, 14548c2ecf20Sopenharmony_ci unsigned int channel, 14558c2ecf20Sopenharmony_ci unsigned char *address) 14568c2ecf20Sopenharmony_ci{ 14578c2ecf20Sopenharmony_ci int index, rv = 0; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 14608c2ecf20Sopenharmony_ci if (!user) 14618c2ecf20Sopenharmony_ci return -ENODEV; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci if (channel >= IPMI_MAX_CHANNELS) { 14648c2ecf20Sopenharmony_ci rv = -EINVAL; 14658c2ecf20Sopenharmony_ci } else { 14668c2ecf20Sopenharmony_ci channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); 14678c2ecf20Sopenharmony_ci *address = user->intf->addrinfo[channel].lun; 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci return rv; 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_my_LUN); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ciint ipmi_get_maintenance_mode(struct ipmi_user *user) 14768c2ecf20Sopenharmony_ci{ 14778c2ecf20Sopenharmony_ci int mode, index; 14788c2ecf20Sopenharmony_ci unsigned long flags; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 14818c2ecf20Sopenharmony_ci if (!user) 14828c2ecf20Sopenharmony_ci return -ENODEV; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags); 14858c2ecf20Sopenharmony_ci mode = user->intf->maintenance_mode; 14868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags); 14878c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci return mode; 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_maintenance_mode); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic void maintenance_mode_update(struct ipmi_smi *intf) 14948c2ecf20Sopenharmony_ci{ 14958c2ecf20Sopenharmony_ci if (intf->handlers->set_maintenance_mode) 14968c2ecf20Sopenharmony_ci intf->handlers->set_maintenance_mode( 14978c2ecf20Sopenharmony_ci intf->send_info, intf->maintenance_mode_enable); 14988c2ecf20Sopenharmony_ci} 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ciint ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) 15018c2ecf20Sopenharmony_ci{ 15028c2ecf20Sopenharmony_ci int rv = 0, index; 15038c2ecf20Sopenharmony_ci unsigned long flags; 15048c2ecf20Sopenharmony_ci struct ipmi_smi *intf = user->intf; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 15078c2ecf20Sopenharmony_ci if (!user) 15088c2ecf20Sopenharmony_ci return -ENODEV; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->maintenance_mode_lock, flags); 15118c2ecf20Sopenharmony_ci if (intf->maintenance_mode != mode) { 15128c2ecf20Sopenharmony_ci switch (mode) { 15138c2ecf20Sopenharmony_ci case IPMI_MAINTENANCE_MODE_AUTO: 15148c2ecf20Sopenharmony_ci intf->maintenance_mode_enable 15158c2ecf20Sopenharmony_ci = (intf->auto_maintenance_timeout > 0); 15168c2ecf20Sopenharmony_ci break; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci case IPMI_MAINTENANCE_MODE_OFF: 15198c2ecf20Sopenharmony_ci intf->maintenance_mode_enable = false; 15208c2ecf20Sopenharmony_ci break; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci case IPMI_MAINTENANCE_MODE_ON: 15238c2ecf20Sopenharmony_ci intf->maintenance_mode_enable = true; 15248c2ecf20Sopenharmony_ci break; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci default: 15278c2ecf20Sopenharmony_ci rv = -EINVAL; 15288c2ecf20Sopenharmony_ci goto out_unlock; 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci intf->maintenance_mode = mode; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci maintenance_mode_update(intf); 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci out_unlock: 15358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); 15368c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci return rv; 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_maintenance_mode); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ciint ipmi_set_gets_events(struct ipmi_user *user, bool val) 15438c2ecf20Sopenharmony_ci{ 15448c2ecf20Sopenharmony_ci unsigned long flags; 15458c2ecf20Sopenharmony_ci struct ipmi_smi *intf = user->intf; 15468c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg, *msg2; 15478c2ecf20Sopenharmony_ci struct list_head msgs; 15488c2ecf20Sopenharmony_ci int index; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 15518c2ecf20Sopenharmony_ci if (!user) 15528c2ecf20Sopenharmony_ci return -ENODEV; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&msgs); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->events_lock, flags); 15578c2ecf20Sopenharmony_ci if (user->gets_events == val) 15588c2ecf20Sopenharmony_ci goto out; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci user->gets_events = val; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci if (val) { 15638c2ecf20Sopenharmony_ci if (atomic_inc_return(&intf->event_waiters) == 1) 15648c2ecf20Sopenharmony_ci need_waiter(intf); 15658c2ecf20Sopenharmony_ci } else { 15668c2ecf20Sopenharmony_ci atomic_dec(&intf->event_waiters); 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci if (intf->delivering_events) 15708c2ecf20Sopenharmony_ci /* 15718c2ecf20Sopenharmony_ci * Another thread is delivering events for this, so 15728c2ecf20Sopenharmony_ci * let it handle any new events. 15738c2ecf20Sopenharmony_ci */ 15748c2ecf20Sopenharmony_ci goto out; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* Deliver any queued events. */ 15778c2ecf20Sopenharmony_ci while (user->gets_events && !list_empty(&intf->waiting_events)) { 15788c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) 15798c2ecf20Sopenharmony_ci list_move_tail(&msg->link, &msgs); 15808c2ecf20Sopenharmony_ci intf->waiting_events_count = 0; 15818c2ecf20Sopenharmony_ci if (intf->event_msg_printed) { 15828c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, "Event queue no longer full\n"); 15838c2ecf20Sopenharmony_ci intf->event_msg_printed = 0; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci intf->delivering_events = 1; 15878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->events_lock, flags); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg, msg2, &msgs, link) { 15908c2ecf20Sopenharmony_ci msg->user = user; 15918c2ecf20Sopenharmony_ci kref_get(&user->refcount); 15928c2ecf20Sopenharmony_ci deliver_local_response(intf, msg); 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->events_lock, flags); 15968c2ecf20Sopenharmony_ci intf->delivering_events = 0; 15978c2ecf20Sopenharmony_ci } 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci out: 16008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->events_lock, flags); 16018c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci return 0; 16048c2ecf20Sopenharmony_ci} 16058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_gets_events); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic struct cmd_rcvr *find_cmd_rcvr(struct ipmi_smi *intf, 16088c2ecf20Sopenharmony_ci unsigned char netfn, 16098c2ecf20Sopenharmony_ci unsigned char cmd, 16108c2ecf20Sopenharmony_ci unsigned char chan) 16118c2ecf20Sopenharmony_ci{ 16128c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link, 16158c2ecf20Sopenharmony_ci lockdep_is_held(&intf->cmd_rcvrs_mutex)) { 16168c2ecf20Sopenharmony_ci if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) 16178c2ecf20Sopenharmony_ci && (rcvr->chans & (1 << chan))) 16188c2ecf20Sopenharmony_ci return rcvr; 16198c2ecf20Sopenharmony_ci } 16208c2ecf20Sopenharmony_ci return NULL; 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_cistatic int is_cmd_rcvr_exclusive(struct ipmi_smi *intf, 16248c2ecf20Sopenharmony_ci unsigned char netfn, 16258c2ecf20Sopenharmony_ci unsigned char cmd, 16268c2ecf20Sopenharmony_ci unsigned int chans) 16278c2ecf20Sopenharmony_ci{ 16288c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link, 16318c2ecf20Sopenharmony_ci lockdep_is_held(&intf->cmd_rcvrs_mutex)) { 16328c2ecf20Sopenharmony_ci if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) 16338c2ecf20Sopenharmony_ci && (rcvr->chans & chans)) 16348c2ecf20Sopenharmony_ci return 0; 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci return 1; 16378c2ecf20Sopenharmony_ci} 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ciint ipmi_register_for_cmd(struct ipmi_user *user, 16408c2ecf20Sopenharmony_ci unsigned char netfn, 16418c2ecf20Sopenharmony_ci unsigned char cmd, 16428c2ecf20Sopenharmony_ci unsigned int chans) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci struct ipmi_smi *intf = user->intf; 16458c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr; 16468c2ecf20Sopenharmony_ci int rv = 0, index; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 16498c2ecf20Sopenharmony_ci if (!user) 16508c2ecf20Sopenharmony_ci return -ENODEV; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL); 16538c2ecf20Sopenharmony_ci if (!rcvr) { 16548c2ecf20Sopenharmony_ci rv = -ENOMEM; 16558c2ecf20Sopenharmony_ci goto out_release; 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci rcvr->cmd = cmd; 16588c2ecf20Sopenharmony_ci rcvr->netfn = netfn; 16598c2ecf20Sopenharmony_ci rcvr->chans = chans; 16608c2ecf20Sopenharmony_ci rcvr->user = user; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci mutex_lock(&intf->cmd_rcvrs_mutex); 16638c2ecf20Sopenharmony_ci /* Make sure the command/netfn is not already registered. */ 16648c2ecf20Sopenharmony_ci if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) { 16658c2ecf20Sopenharmony_ci rv = -EBUSY; 16668c2ecf20Sopenharmony_ci goto out_unlock; 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ciout_unlock: 16748c2ecf20Sopenharmony_ci mutex_unlock(&intf->cmd_rcvrs_mutex); 16758c2ecf20Sopenharmony_ci if (rv) 16768c2ecf20Sopenharmony_ci kfree(rcvr); 16778c2ecf20Sopenharmony_ciout_release: 16788c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci return rv; 16818c2ecf20Sopenharmony_ci} 16828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_register_for_cmd); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ciint ipmi_unregister_for_cmd(struct ipmi_user *user, 16858c2ecf20Sopenharmony_ci unsigned char netfn, 16868c2ecf20Sopenharmony_ci unsigned char cmd, 16878c2ecf20Sopenharmony_ci unsigned int chans) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci struct ipmi_smi *intf = user->intf; 16908c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr; 16918c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvrs = NULL; 16928c2ecf20Sopenharmony_ci int i, rv = -ENOENT, index; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 16958c2ecf20Sopenharmony_ci if (!user) 16968c2ecf20Sopenharmony_ci return -ENODEV; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci mutex_lock(&intf->cmd_rcvrs_mutex); 16998c2ecf20Sopenharmony_ci for (i = 0; i < IPMI_NUM_CHANNELS; i++) { 17008c2ecf20Sopenharmony_ci if (((1 << i) & chans) == 0) 17018c2ecf20Sopenharmony_ci continue; 17028c2ecf20Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, i); 17038c2ecf20Sopenharmony_ci if (rcvr == NULL) 17048c2ecf20Sopenharmony_ci continue; 17058c2ecf20Sopenharmony_ci if (rcvr->user == user) { 17068c2ecf20Sopenharmony_ci rv = 0; 17078c2ecf20Sopenharmony_ci rcvr->chans &= ~chans; 17088c2ecf20Sopenharmony_ci if (rcvr->chans == 0) { 17098c2ecf20Sopenharmony_ci list_del_rcu(&rcvr->link); 17108c2ecf20Sopenharmony_ci rcvr->next = rcvrs; 17118c2ecf20Sopenharmony_ci rcvrs = rcvr; 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci } 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci mutex_unlock(&intf->cmd_rcvrs_mutex); 17168c2ecf20Sopenharmony_ci synchronize_rcu(); 17178c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 17188c2ecf20Sopenharmony_ci while (rcvrs) { 17198c2ecf20Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS); 17208c2ecf20Sopenharmony_ci rcvr = rcvrs; 17218c2ecf20Sopenharmony_ci rcvrs = rcvr->next; 17228c2ecf20Sopenharmony_ci kfree(rcvr); 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci return rv; 17268c2ecf20Sopenharmony_ci} 17278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_unregister_for_cmd); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_cistatic unsigned char 17308c2ecf20Sopenharmony_ciipmb_checksum(unsigned char *data, int size) 17318c2ecf20Sopenharmony_ci{ 17328c2ecf20Sopenharmony_ci unsigned char csum = 0; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci for (; size > 0; size--, data++) 17358c2ecf20Sopenharmony_ci csum += *data; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci return -csum; 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_cistatic inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg, 17418c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg, 17428c2ecf20Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr, 17438c2ecf20Sopenharmony_ci long msgid, 17448c2ecf20Sopenharmony_ci unsigned char ipmb_seq, 17458c2ecf20Sopenharmony_ci int broadcast, 17468c2ecf20Sopenharmony_ci unsigned char source_address, 17478c2ecf20Sopenharmony_ci unsigned char source_lun) 17488c2ecf20Sopenharmony_ci{ 17498c2ecf20Sopenharmony_ci int i = broadcast; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci /* Format the IPMB header data. */ 17528c2ecf20Sopenharmony_ci smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 17538c2ecf20Sopenharmony_ci smi_msg->data[1] = IPMI_SEND_MSG_CMD; 17548c2ecf20Sopenharmony_ci smi_msg->data[2] = ipmb_addr->channel; 17558c2ecf20Sopenharmony_ci if (broadcast) 17568c2ecf20Sopenharmony_ci smi_msg->data[3] = 0; 17578c2ecf20Sopenharmony_ci smi_msg->data[i+3] = ipmb_addr->slave_addr; 17588c2ecf20Sopenharmony_ci smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3); 17598c2ecf20Sopenharmony_ci smi_msg->data[i+5] = ipmb_checksum(&smi_msg->data[i + 3], 2); 17608c2ecf20Sopenharmony_ci smi_msg->data[i+6] = source_address; 17618c2ecf20Sopenharmony_ci smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun; 17628c2ecf20Sopenharmony_ci smi_msg->data[i+8] = msg->cmd; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci /* Now tack on the data to the message. */ 17658c2ecf20Sopenharmony_ci if (msg->data_len > 0) 17668c2ecf20Sopenharmony_ci memcpy(&smi_msg->data[i + 9], msg->data, msg->data_len); 17678c2ecf20Sopenharmony_ci smi_msg->data_size = msg->data_len + 9; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci /* Now calculate the checksum and tack it on. */ 17708c2ecf20Sopenharmony_ci smi_msg->data[i+smi_msg->data_size] 17718c2ecf20Sopenharmony_ci = ipmb_checksum(&smi_msg->data[i + 6], smi_msg->data_size - 6); 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci /* 17748c2ecf20Sopenharmony_ci * Add on the checksum size and the offset from the 17758c2ecf20Sopenharmony_ci * broadcast. 17768c2ecf20Sopenharmony_ci */ 17778c2ecf20Sopenharmony_ci smi_msg->data_size += 1 + i; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci smi_msg->msgid = msgid; 17808c2ecf20Sopenharmony_ci} 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_cistatic inline void format_lan_msg(struct ipmi_smi_msg *smi_msg, 17838c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg, 17848c2ecf20Sopenharmony_ci struct ipmi_lan_addr *lan_addr, 17858c2ecf20Sopenharmony_ci long msgid, 17868c2ecf20Sopenharmony_ci unsigned char ipmb_seq, 17878c2ecf20Sopenharmony_ci unsigned char source_lun) 17888c2ecf20Sopenharmony_ci{ 17898c2ecf20Sopenharmony_ci /* Format the IPMB header data. */ 17908c2ecf20Sopenharmony_ci smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 17918c2ecf20Sopenharmony_ci smi_msg->data[1] = IPMI_SEND_MSG_CMD; 17928c2ecf20Sopenharmony_ci smi_msg->data[2] = lan_addr->channel; 17938c2ecf20Sopenharmony_ci smi_msg->data[3] = lan_addr->session_handle; 17948c2ecf20Sopenharmony_ci smi_msg->data[4] = lan_addr->remote_SWID; 17958c2ecf20Sopenharmony_ci smi_msg->data[5] = (msg->netfn << 2) | (lan_addr->lun & 0x3); 17968c2ecf20Sopenharmony_ci smi_msg->data[6] = ipmb_checksum(&smi_msg->data[4], 2); 17978c2ecf20Sopenharmony_ci smi_msg->data[7] = lan_addr->local_SWID; 17988c2ecf20Sopenharmony_ci smi_msg->data[8] = (ipmb_seq << 2) | source_lun; 17998c2ecf20Sopenharmony_ci smi_msg->data[9] = msg->cmd; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci /* Now tack on the data to the message. */ 18028c2ecf20Sopenharmony_ci if (msg->data_len > 0) 18038c2ecf20Sopenharmony_ci memcpy(&smi_msg->data[10], msg->data, msg->data_len); 18048c2ecf20Sopenharmony_ci smi_msg->data_size = msg->data_len + 10; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci /* Now calculate the checksum and tack it on. */ 18078c2ecf20Sopenharmony_ci smi_msg->data[smi_msg->data_size] 18088c2ecf20Sopenharmony_ci = ipmb_checksum(&smi_msg->data[7], smi_msg->data_size - 7); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci /* 18118c2ecf20Sopenharmony_ci * Add on the checksum size and the offset from the 18128c2ecf20Sopenharmony_ci * broadcast. 18138c2ecf20Sopenharmony_ci */ 18148c2ecf20Sopenharmony_ci smi_msg->data_size += 1; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci smi_msg->msgid = msgid; 18178c2ecf20Sopenharmony_ci} 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_cistatic struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf, 18208c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 18218c2ecf20Sopenharmony_ci int priority) 18228c2ecf20Sopenharmony_ci{ 18238c2ecf20Sopenharmony_ci if (intf->curr_msg) { 18248c2ecf20Sopenharmony_ci if (priority > 0) 18258c2ecf20Sopenharmony_ci list_add_tail(&smi_msg->link, &intf->hp_xmit_msgs); 18268c2ecf20Sopenharmony_ci else 18278c2ecf20Sopenharmony_ci list_add_tail(&smi_msg->link, &intf->xmit_msgs); 18288c2ecf20Sopenharmony_ci smi_msg = NULL; 18298c2ecf20Sopenharmony_ci } else { 18308c2ecf20Sopenharmony_ci intf->curr_msg = smi_msg; 18318c2ecf20Sopenharmony_ci } 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci return smi_msg; 18348c2ecf20Sopenharmony_ci} 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_cistatic void smi_send(struct ipmi_smi *intf, 18378c2ecf20Sopenharmony_ci const struct ipmi_smi_handlers *handlers, 18388c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg, int priority) 18398c2ecf20Sopenharmony_ci{ 18408c2ecf20Sopenharmony_ci int run_to_completion = intf->run_to_completion; 18418c2ecf20Sopenharmony_ci unsigned long flags = 0; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if (!run_to_completion) 18448c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->xmit_msgs_lock, flags); 18458c2ecf20Sopenharmony_ci smi_msg = smi_add_send_msg(intf, smi_msg, priority); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci if (!run_to_completion) 18488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci if (smi_msg) 18518c2ecf20Sopenharmony_ci handlers->sender(intf->send_info, smi_msg); 18528c2ecf20Sopenharmony_ci} 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_cistatic bool is_maintenance_mode_cmd(struct kernel_ipmi_msg *msg) 18558c2ecf20Sopenharmony_ci{ 18568c2ecf20Sopenharmony_ci return (((msg->netfn == IPMI_NETFN_APP_REQUEST) 18578c2ecf20Sopenharmony_ci && ((msg->cmd == IPMI_COLD_RESET_CMD) 18588c2ecf20Sopenharmony_ci || (msg->cmd == IPMI_WARM_RESET_CMD))) 18598c2ecf20Sopenharmony_ci || (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST)); 18608c2ecf20Sopenharmony_ci} 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_cistatic int i_ipmi_req_sysintf(struct ipmi_smi *intf, 18638c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 18648c2ecf20Sopenharmony_ci long msgid, 18658c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg, 18668c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 18678c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 18688c2ecf20Sopenharmony_ci int retries, 18698c2ecf20Sopenharmony_ci unsigned int retry_time_ms) 18708c2ecf20Sopenharmony_ci{ 18718c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (msg->netfn & 1) 18748c2ecf20Sopenharmony_ci /* Responses are not allowed to the SMI. */ 18758c2ecf20Sopenharmony_ci return -EINVAL; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci smi_addr = (struct ipmi_system_interface_addr *) addr; 18788c2ecf20Sopenharmony_ci if (smi_addr->lun > 3) { 18798c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 18808c2ecf20Sopenharmony_ci return -EINVAL; 18818c2ecf20Sopenharmony_ci } 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr)); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci if ((msg->netfn == IPMI_NETFN_APP_REQUEST) 18868c2ecf20Sopenharmony_ci && ((msg->cmd == IPMI_SEND_MSG_CMD) 18878c2ecf20Sopenharmony_ci || (msg->cmd == IPMI_GET_MSG_CMD) 18888c2ecf20Sopenharmony_ci || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) { 18898c2ecf20Sopenharmony_ci /* 18908c2ecf20Sopenharmony_ci * We don't let the user do these, since we manage 18918c2ecf20Sopenharmony_ci * the sequence numbers. 18928c2ecf20Sopenharmony_ci */ 18938c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 18948c2ecf20Sopenharmony_ci return -EINVAL; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci if (is_maintenance_mode_cmd(msg)) { 18988c2ecf20Sopenharmony_ci unsigned long flags; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->maintenance_mode_lock, flags); 19018c2ecf20Sopenharmony_ci intf->auto_maintenance_timeout 19028c2ecf20Sopenharmony_ci = maintenance_mode_timeout_ms; 19038c2ecf20Sopenharmony_ci if (!intf->maintenance_mode 19048c2ecf20Sopenharmony_ci && !intf->maintenance_mode_enable) { 19058c2ecf20Sopenharmony_ci intf->maintenance_mode_enable = true; 19068c2ecf20Sopenharmony_ci maintenance_mode_update(intf); 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->maintenance_mode_lock, 19098c2ecf20Sopenharmony_ci flags); 19108c2ecf20Sopenharmony_ci } 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci if (msg->data_len + 2 > IPMI_MAX_MSG_LENGTH) { 19138c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 19148c2ecf20Sopenharmony_ci return -EMSGSIZE; 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3); 19188c2ecf20Sopenharmony_ci smi_msg->data[1] = msg->cmd; 19198c2ecf20Sopenharmony_ci smi_msg->msgid = msgid; 19208c2ecf20Sopenharmony_ci smi_msg->user_data = recv_msg; 19218c2ecf20Sopenharmony_ci if (msg->data_len > 0) 19228c2ecf20Sopenharmony_ci memcpy(&smi_msg->data[2], msg->data, msg->data_len); 19238c2ecf20Sopenharmony_ci smi_msg->data_size = msg->data_len + 2; 19248c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_local_commands); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci return 0; 19278c2ecf20Sopenharmony_ci} 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_cistatic int i_ipmi_req_ipmb(struct ipmi_smi *intf, 19308c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 19318c2ecf20Sopenharmony_ci long msgid, 19328c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg, 19338c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 19348c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 19358c2ecf20Sopenharmony_ci unsigned char source_address, 19368c2ecf20Sopenharmony_ci unsigned char source_lun, 19378c2ecf20Sopenharmony_ci int retries, 19388c2ecf20Sopenharmony_ci unsigned int retry_time_ms) 19398c2ecf20Sopenharmony_ci{ 19408c2ecf20Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr; 19418c2ecf20Sopenharmony_ci unsigned char ipmb_seq; 19428c2ecf20Sopenharmony_ci long seqid; 19438c2ecf20Sopenharmony_ci int broadcast = 0; 19448c2ecf20Sopenharmony_ci struct ipmi_channel *chans; 19458c2ecf20Sopenharmony_ci int rv = 0; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci if (addr->channel >= IPMI_MAX_CHANNELS) { 19488c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 19498c2ecf20Sopenharmony_ci return -EINVAL; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci chans = READ_ONCE(intf->channel_list)->c; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) { 19558c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 19568c2ecf20Sopenharmony_ci return -EINVAL; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) { 19608c2ecf20Sopenharmony_ci /* 19618c2ecf20Sopenharmony_ci * Broadcasts add a zero at the beginning of the 19628c2ecf20Sopenharmony_ci * message, but otherwise is the same as an IPMB 19638c2ecf20Sopenharmony_ci * address. 19648c2ecf20Sopenharmony_ci */ 19658c2ecf20Sopenharmony_ci addr->addr_type = IPMI_IPMB_ADDR_TYPE; 19668c2ecf20Sopenharmony_ci broadcast = 1; 19678c2ecf20Sopenharmony_ci retries = 0; /* Don't retry broadcasts. */ 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci /* 19718c2ecf20Sopenharmony_ci * 9 for the header and 1 for the checksum, plus 19728c2ecf20Sopenharmony_ci * possibly one for the broadcast. 19738c2ecf20Sopenharmony_ci */ 19748c2ecf20Sopenharmony_ci if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) { 19758c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 19768c2ecf20Sopenharmony_ci return -EMSGSIZE; 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci ipmb_addr = (struct ipmi_ipmb_addr *) addr; 19808c2ecf20Sopenharmony_ci if (ipmb_addr->lun > 3) { 19818c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 19828c2ecf20Sopenharmony_ci return -EINVAL; 19838c2ecf20Sopenharmony_ci } 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr)); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci if (recv_msg->msg.netfn & 0x1) { 19888c2ecf20Sopenharmony_ci /* 19898c2ecf20Sopenharmony_ci * It's a response, so use the user's sequence 19908c2ecf20Sopenharmony_ci * from msgid. 19918c2ecf20Sopenharmony_ci */ 19928c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_ipmb_responses); 19938c2ecf20Sopenharmony_ci format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid, 19948c2ecf20Sopenharmony_ci msgid, broadcast, 19958c2ecf20Sopenharmony_ci source_address, source_lun); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci /* 19988c2ecf20Sopenharmony_ci * Save the receive message so we can use it 19998c2ecf20Sopenharmony_ci * to deliver the response. 20008c2ecf20Sopenharmony_ci */ 20018c2ecf20Sopenharmony_ci smi_msg->user_data = recv_msg; 20028c2ecf20Sopenharmony_ci } else { 20038c2ecf20Sopenharmony_ci /* It's a command, so get a sequence for it. */ 20048c2ecf20Sopenharmony_ci unsigned long flags; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci if (is_maintenance_mode_cmd(msg)) 20098c2ecf20Sopenharmony_ci intf->ipmb_maintenance_mode_timeout = 20108c2ecf20Sopenharmony_ci maintenance_mode_timeout_ms; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci if (intf->ipmb_maintenance_mode_timeout && retry_time_ms == 0) 20138c2ecf20Sopenharmony_ci /* Different default in maintenance mode */ 20148c2ecf20Sopenharmony_ci retry_time_ms = default_maintenance_retry_ms; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* 20178c2ecf20Sopenharmony_ci * Create a sequence number with a 1 second 20188c2ecf20Sopenharmony_ci * timeout and 4 retries. 20198c2ecf20Sopenharmony_ci */ 20208c2ecf20Sopenharmony_ci rv = intf_next_seq(intf, 20218c2ecf20Sopenharmony_ci recv_msg, 20228c2ecf20Sopenharmony_ci retry_time_ms, 20238c2ecf20Sopenharmony_ci retries, 20248c2ecf20Sopenharmony_ci broadcast, 20258c2ecf20Sopenharmony_ci &ipmb_seq, 20268c2ecf20Sopenharmony_ci &seqid); 20278c2ecf20Sopenharmony_ci if (rv) 20288c2ecf20Sopenharmony_ci /* 20298c2ecf20Sopenharmony_ci * We have used up all the sequence numbers, 20308c2ecf20Sopenharmony_ci * probably, so abort. 20318c2ecf20Sopenharmony_ci */ 20328c2ecf20Sopenharmony_ci goto out_err; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_ipmb_commands); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci /* 20378c2ecf20Sopenharmony_ci * Store the sequence number in the message, 20388c2ecf20Sopenharmony_ci * so that when the send message response 20398c2ecf20Sopenharmony_ci * comes back we can start the timer. 20408c2ecf20Sopenharmony_ci */ 20418c2ecf20Sopenharmony_ci format_ipmb_msg(smi_msg, msg, ipmb_addr, 20428c2ecf20Sopenharmony_ci STORE_SEQ_IN_MSGID(ipmb_seq, seqid), 20438c2ecf20Sopenharmony_ci ipmb_seq, broadcast, 20448c2ecf20Sopenharmony_ci source_address, source_lun); 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci /* 20478c2ecf20Sopenharmony_ci * Copy the message into the recv message data, so we 20488c2ecf20Sopenharmony_ci * can retransmit it later if necessary. 20498c2ecf20Sopenharmony_ci */ 20508c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, smi_msg->data, 20518c2ecf20Sopenharmony_ci smi_msg->data_size); 20528c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 20538c2ecf20Sopenharmony_ci recv_msg->msg.data_len = smi_msg->data_size; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci /* 20568c2ecf20Sopenharmony_ci * We don't unlock until here, because we need 20578c2ecf20Sopenharmony_ci * to copy the completed message into the 20588c2ecf20Sopenharmony_ci * recv_msg before we release the lock. 20598c2ecf20Sopenharmony_ci * Otherwise, race conditions may bite us. I 20608c2ecf20Sopenharmony_ci * know that's pretty paranoid, but I prefer 20618c2ecf20Sopenharmony_ci * to be correct. 20628c2ecf20Sopenharmony_ci */ 20638c2ecf20Sopenharmony_ciout_err: 20648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 20658c2ecf20Sopenharmony_ci } 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci return rv; 20688c2ecf20Sopenharmony_ci} 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_cistatic int i_ipmi_req_lan(struct ipmi_smi *intf, 20718c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 20728c2ecf20Sopenharmony_ci long msgid, 20738c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg, 20748c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg, 20758c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg, 20768c2ecf20Sopenharmony_ci unsigned char source_lun, 20778c2ecf20Sopenharmony_ci int retries, 20788c2ecf20Sopenharmony_ci unsigned int retry_time_ms) 20798c2ecf20Sopenharmony_ci{ 20808c2ecf20Sopenharmony_ci struct ipmi_lan_addr *lan_addr; 20818c2ecf20Sopenharmony_ci unsigned char ipmb_seq; 20828c2ecf20Sopenharmony_ci long seqid; 20838c2ecf20Sopenharmony_ci struct ipmi_channel *chans; 20848c2ecf20Sopenharmony_ci int rv = 0; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci if (addr->channel >= IPMI_MAX_CHANNELS) { 20878c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 20888c2ecf20Sopenharmony_ci return -EINVAL; 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci chans = READ_ONCE(intf->channel_list)->c; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if ((chans[addr->channel].medium 20948c2ecf20Sopenharmony_ci != IPMI_CHANNEL_MEDIUM_8023LAN) 20958c2ecf20Sopenharmony_ci && (chans[addr->channel].medium 20968c2ecf20Sopenharmony_ci != IPMI_CHANNEL_MEDIUM_ASYNC)) { 20978c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 20988c2ecf20Sopenharmony_ci return -EINVAL; 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci /* 11 for the header and 1 for the checksum. */ 21028c2ecf20Sopenharmony_ci if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) { 21038c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 21048c2ecf20Sopenharmony_ci return -EMSGSIZE; 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci lan_addr = (struct ipmi_lan_addr *) addr; 21088c2ecf20Sopenharmony_ci if (lan_addr->lun > 3) { 21098c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 21108c2ecf20Sopenharmony_ci return -EINVAL; 21118c2ecf20Sopenharmony_ci } 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr)); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci if (recv_msg->msg.netfn & 0x1) { 21168c2ecf20Sopenharmony_ci /* 21178c2ecf20Sopenharmony_ci * It's a response, so use the user's sequence 21188c2ecf20Sopenharmony_ci * from msgid. 21198c2ecf20Sopenharmony_ci */ 21208c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_lan_responses); 21218c2ecf20Sopenharmony_ci format_lan_msg(smi_msg, msg, lan_addr, msgid, 21228c2ecf20Sopenharmony_ci msgid, source_lun); 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci /* 21258c2ecf20Sopenharmony_ci * Save the receive message so we can use it 21268c2ecf20Sopenharmony_ci * to deliver the response. 21278c2ecf20Sopenharmony_ci */ 21288c2ecf20Sopenharmony_ci smi_msg->user_data = recv_msg; 21298c2ecf20Sopenharmony_ci } else { 21308c2ecf20Sopenharmony_ci /* It's a command, so get a sequence for it. */ 21318c2ecf20Sopenharmony_ci unsigned long flags; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci /* 21368c2ecf20Sopenharmony_ci * Create a sequence number with a 1 second 21378c2ecf20Sopenharmony_ci * timeout and 4 retries. 21388c2ecf20Sopenharmony_ci */ 21398c2ecf20Sopenharmony_ci rv = intf_next_seq(intf, 21408c2ecf20Sopenharmony_ci recv_msg, 21418c2ecf20Sopenharmony_ci retry_time_ms, 21428c2ecf20Sopenharmony_ci retries, 21438c2ecf20Sopenharmony_ci 0, 21448c2ecf20Sopenharmony_ci &ipmb_seq, 21458c2ecf20Sopenharmony_ci &seqid); 21468c2ecf20Sopenharmony_ci if (rv) 21478c2ecf20Sopenharmony_ci /* 21488c2ecf20Sopenharmony_ci * We have used up all the sequence numbers, 21498c2ecf20Sopenharmony_ci * probably, so abort. 21508c2ecf20Sopenharmony_ci */ 21518c2ecf20Sopenharmony_ci goto out_err; 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_lan_commands); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci /* 21568c2ecf20Sopenharmony_ci * Store the sequence number in the message, 21578c2ecf20Sopenharmony_ci * so that when the send message response 21588c2ecf20Sopenharmony_ci * comes back we can start the timer. 21598c2ecf20Sopenharmony_ci */ 21608c2ecf20Sopenharmony_ci format_lan_msg(smi_msg, msg, lan_addr, 21618c2ecf20Sopenharmony_ci STORE_SEQ_IN_MSGID(ipmb_seq, seqid), 21628c2ecf20Sopenharmony_ci ipmb_seq, source_lun); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci /* 21658c2ecf20Sopenharmony_ci * Copy the message into the recv message data, so we 21668c2ecf20Sopenharmony_ci * can retransmit it later if necessary. 21678c2ecf20Sopenharmony_ci */ 21688c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, smi_msg->data, 21698c2ecf20Sopenharmony_ci smi_msg->data_size); 21708c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 21718c2ecf20Sopenharmony_ci recv_msg->msg.data_len = smi_msg->data_size; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* 21748c2ecf20Sopenharmony_ci * We don't unlock until here, because we need 21758c2ecf20Sopenharmony_ci * to copy the completed message into the 21768c2ecf20Sopenharmony_ci * recv_msg before we release the lock. 21778c2ecf20Sopenharmony_ci * Otherwise, race conditions may bite us. I 21788c2ecf20Sopenharmony_ci * know that's pretty paranoid, but I prefer 21798c2ecf20Sopenharmony_ci * to be correct. 21808c2ecf20Sopenharmony_ci */ 21818c2ecf20Sopenharmony_ciout_err: 21828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 21838c2ecf20Sopenharmony_ci } 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci return rv; 21868c2ecf20Sopenharmony_ci} 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci/* 21898c2ecf20Sopenharmony_ci * Separate from ipmi_request so that the user does not have to be 21908c2ecf20Sopenharmony_ci * supplied in certain circumstances (mainly at panic time). If 21918c2ecf20Sopenharmony_ci * messages are supplied, they will be freed, even if an error 21928c2ecf20Sopenharmony_ci * occurs. 21938c2ecf20Sopenharmony_ci */ 21948c2ecf20Sopenharmony_cistatic int i_ipmi_request(struct ipmi_user *user, 21958c2ecf20Sopenharmony_ci struct ipmi_smi *intf, 21968c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 21978c2ecf20Sopenharmony_ci long msgid, 21988c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg, 21998c2ecf20Sopenharmony_ci void *user_msg_data, 22008c2ecf20Sopenharmony_ci void *supplied_smi, 22018c2ecf20Sopenharmony_ci struct ipmi_recv_msg *supplied_recv, 22028c2ecf20Sopenharmony_ci int priority, 22038c2ecf20Sopenharmony_ci unsigned char source_address, 22048c2ecf20Sopenharmony_ci unsigned char source_lun, 22058c2ecf20Sopenharmony_ci int retries, 22068c2ecf20Sopenharmony_ci unsigned int retry_time_ms) 22078c2ecf20Sopenharmony_ci{ 22088c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg; 22098c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 22108c2ecf20Sopenharmony_ci int rv = 0; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci if (supplied_recv) 22138c2ecf20Sopenharmony_ci recv_msg = supplied_recv; 22148c2ecf20Sopenharmony_ci else { 22158c2ecf20Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 22168c2ecf20Sopenharmony_ci if (recv_msg == NULL) { 22178c2ecf20Sopenharmony_ci rv = -ENOMEM; 22188c2ecf20Sopenharmony_ci goto out; 22198c2ecf20Sopenharmony_ci } 22208c2ecf20Sopenharmony_ci } 22218c2ecf20Sopenharmony_ci recv_msg->user_msg_data = user_msg_data; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci if (supplied_smi) 22248c2ecf20Sopenharmony_ci smi_msg = (struct ipmi_smi_msg *) supplied_smi; 22258c2ecf20Sopenharmony_ci else { 22268c2ecf20Sopenharmony_ci smi_msg = ipmi_alloc_smi_msg(); 22278c2ecf20Sopenharmony_ci if (smi_msg == NULL) { 22288c2ecf20Sopenharmony_ci if (!supplied_recv) 22298c2ecf20Sopenharmony_ci ipmi_free_recv_msg(recv_msg); 22308c2ecf20Sopenharmony_ci rv = -ENOMEM; 22318c2ecf20Sopenharmony_ci goto out; 22328c2ecf20Sopenharmony_ci } 22338c2ecf20Sopenharmony_ci } 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci rcu_read_lock(); 22368c2ecf20Sopenharmony_ci if (intf->in_shutdown) { 22378c2ecf20Sopenharmony_ci rv = -ENODEV; 22388c2ecf20Sopenharmony_ci goto out_err; 22398c2ecf20Sopenharmony_ci } 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci recv_msg->user = user; 22428c2ecf20Sopenharmony_ci if (user) 22438c2ecf20Sopenharmony_ci /* The put happens when the message is freed. */ 22448c2ecf20Sopenharmony_ci kref_get(&user->refcount); 22458c2ecf20Sopenharmony_ci recv_msg->msgid = msgid; 22468c2ecf20Sopenharmony_ci /* 22478c2ecf20Sopenharmony_ci * Store the message to send in the receive message so timeout 22488c2ecf20Sopenharmony_ci * responses can get the proper response data. 22498c2ecf20Sopenharmony_ci */ 22508c2ecf20Sopenharmony_ci recv_msg->msg = *msg; 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { 22538c2ecf20Sopenharmony_ci rv = i_ipmi_req_sysintf(intf, addr, msgid, msg, smi_msg, 22548c2ecf20Sopenharmony_ci recv_msg, retries, retry_time_ms); 22558c2ecf20Sopenharmony_ci } else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { 22568c2ecf20Sopenharmony_ci rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg, 22578c2ecf20Sopenharmony_ci source_address, source_lun, 22588c2ecf20Sopenharmony_ci retries, retry_time_ms); 22598c2ecf20Sopenharmony_ci } else if (is_lan_addr(addr)) { 22608c2ecf20Sopenharmony_ci rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg, 22618c2ecf20Sopenharmony_ci source_lun, retries, retry_time_ms); 22628c2ecf20Sopenharmony_ci } else { 22638c2ecf20Sopenharmony_ci /* Unknown address type. */ 22648c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_invalid_commands); 22658c2ecf20Sopenharmony_ci rv = -EINVAL; 22668c2ecf20Sopenharmony_ci } 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci if (rv) { 22698c2ecf20Sopenharmony_ciout_err: 22708c2ecf20Sopenharmony_ci ipmi_free_smi_msg(smi_msg); 22718c2ecf20Sopenharmony_ci ipmi_free_recv_msg(recv_msg); 22728c2ecf20Sopenharmony_ci } else { 22738c2ecf20Sopenharmony_ci pr_debug("Send: %*ph\n", smi_msg->data_size, smi_msg->data); 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci smi_send(intf, intf->handlers, smi_msg, priority); 22768c2ecf20Sopenharmony_ci } 22778c2ecf20Sopenharmony_ci rcu_read_unlock(); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ciout: 22808c2ecf20Sopenharmony_ci return rv; 22818c2ecf20Sopenharmony_ci} 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_cistatic int check_addr(struct ipmi_smi *intf, 22848c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 22858c2ecf20Sopenharmony_ci unsigned char *saddr, 22868c2ecf20Sopenharmony_ci unsigned char *lun) 22878c2ecf20Sopenharmony_ci{ 22888c2ecf20Sopenharmony_ci if (addr->channel >= IPMI_MAX_CHANNELS) 22898c2ecf20Sopenharmony_ci return -EINVAL; 22908c2ecf20Sopenharmony_ci addr->channel = array_index_nospec(addr->channel, IPMI_MAX_CHANNELS); 22918c2ecf20Sopenharmony_ci *lun = intf->addrinfo[addr->channel].lun; 22928c2ecf20Sopenharmony_ci *saddr = intf->addrinfo[addr->channel].address; 22938c2ecf20Sopenharmony_ci return 0; 22948c2ecf20Sopenharmony_ci} 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ciint ipmi_request_settime(struct ipmi_user *user, 22978c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 22988c2ecf20Sopenharmony_ci long msgid, 22998c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg, 23008c2ecf20Sopenharmony_ci void *user_msg_data, 23018c2ecf20Sopenharmony_ci int priority, 23028c2ecf20Sopenharmony_ci int retries, 23038c2ecf20Sopenharmony_ci unsigned int retry_time_ms) 23048c2ecf20Sopenharmony_ci{ 23058c2ecf20Sopenharmony_ci unsigned char saddr = 0, lun = 0; 23068c2ecf20Sopenharmony_ci int rv, index; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci if (!user) 23098c2ecf20Sopenharmony_ci return -EINVAL; 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 23128c2ecf20Sopenharmony_ci if (!user) 23138c2ecf20Sopenharmony_ci return -ENODEV; 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci rv = check_addr(user->intf, addr, &saddr, &lun); 23168c2ecf20Sopenharmony_ci if (!rv) 23178c2ecf20Sopenharmony_ci rv = i_ipmi_request(user, 23188c2ecf20Sopenharmony_ci user->intf, 23198c2ecf20Sopenharmony_ci addr, 23208c2ecf20Sopenharmony_ci msgid, 23218c2ecf20Sopenharmony_ci msg, 23228c2ecf20Sopenharmony_ci user_msg_data, 23238c2ecf20Sopenharmony_ci NULL, NULL, 23248c2ecf20Sopenharmony_ci priority, 23258c2ecf20Sopenharmony_ci saddr, 23268c2ecf20Sopenharmony_ci lun, 23278c2ecf20Sopenharmony_ci retries, 23288c2ecf20Sopenharmony_ci retry_time_ms); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 23318c2ecf20Sopenharmony_ci return rv; 23328c2ecf20Sopenharmony_ci} 23338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_request_settime); 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ciint ipmi_request_supply_msgs(struct ipmi_user *user, 23368c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 23378c2ecf20Sopenharmony_ci long msgid, 23388c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg, 23398c2ecf20Sopenharmony_ci void *user_msg_data, 23408c2ecf20Sopenharmony_ci void *supplied_smi, 23418c2ecf20Sopenharmony_ci struct ipmi_recv_msg *supplied_recv, 23428c2ecf20Sopenharmony_ci int priority) 23438c2ecf20Sopenharmony_ci{ 23448c2ecf20Sopenharmony_ci unsigned char saddr = 0, lun = 0; 23458c2ecf20Sopenharmony_ci int rv, index; 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci if (!user) 23488c2ecf20Sopenharmony_ci return -EINVAL; 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci user = acquire_ipmi_user(user, &index); 23518c2ecf20Sopenharmony_ci if (!user) 23528c2ecf20Sopenharmony_ci return -ENODEV; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci rv = check_addr(user->intf, addr, &saddr, &lun); 23558c2ecf20Sopenharmony_ci if (!rv) 23568c2ecf20Sopenharmony_ci rv = i_ipmi_request(user, 23578c2ecf20Sopenharmony_ci user->intf, 23588c2ecf20Sopenharmony_ci addr, 23598c2ecf20Sopenharmony_ci msgid, 23608c2ecf20Sopenharmony_ci msg, 23618c2ecf20Sopenharmony_ci user_msg_data, 23628c2ecf20Sopenharmony_ci supplied_smi, 23638c2ecf20Sopenharmony_ci supplied_recv, 23648c2ecf20Sopenharmony_ci priority, 23658c2ecf20Sopenharmony_ci saddr, 23668c2ecf20Sopenharmony_ci lun, 23678c2ecf20Sopenharmony_ci -1, 0); 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci release_ipmi_user(user, index); 23708c2ecf20Sopenharmony_ci return rv; 23718c2ecf20Sopenharmony_ci} 23728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_request_supply_msgs); 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_cistatic void bmc_device_id_handler(struct ipmi_smi *intf, 23758c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg) 23768c2ecf20Sopenharmony_ci{ 23778c2ecf20Sopenharmony_ci int rv; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 23808c2ecf20Sopenharmony_ci || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) 23818c2ecf20Sopenharmony_ci || (msg->msg.cmd != IPMI_GET_DEVICE_ID_CMD)) { 23828c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 23838c2ecf20Sopenharmony_ci "invalid device_id msg: addr_type=%d netfn=%x cmd=%x\n", 23848c2ecf20Sopenharmony_ci msg->addr.addr_type, msg->msg.netfn, msg->msg.cmd); 23858c2ecf20Sopenharmony_ci return; 23868c2ecf20Sopenharmony_ci } 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd, 23898c2ecf20Sopenharmony_ci msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id); 23908c2ecf20Sopenharmony_ci if (rv) { 23918c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, "device id demangle failed: %d\n", rv); 23928c2ecf20Sopenharmony_ci /* record completion code when error */ 23938c2ecf20Sopenharmony_ci intf->bmc->cc = msg->msg.data[0]; 23948c2ecf20Sopenharmony_ci intf->bmc->dyn_id_set = 0; 23958c2ecf20Sopenharmony_ci } else { 23968c2ecf20Sopenharmony_ci /* 23978c2ecf20Sopenharmony_ci * Make sure the id data is available before setting 23988c2ecf20Sopenharmony_ci * dyn_id_set. 23998c2ecf20Sopenharmony_ci */ 24008c2ecf20Sopenharmony_ci smp_wmb(); 24018c2ecf20Sopenharmony_ci intf->bmc->dyn_id_set = 1; 24028c2ecf20Sopenharmony_ci } 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci wake_up(&intf->waitq); 24058c2ecf20Sopenharmony_ci} 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_cistatic int 24088c2ecf20Sopenharmony_cisend_get_device_id_cmd(struct ipmi_smi *intf) 24098c2ecf20Sopenharmony_ci{ 24108c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr si; 24118c2ecf20Sopenharmony_ci struct kernel_ipmi_msg msg; 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 24148c2ecf20Sopenharmony_ci si.channel = IPMI_BMC_CHANNEL; 24158c2ecf20Sopenharmony_ci si.lun = 0; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci msg.netfn = IPMI_NETFN_APP_REQUEST; 24188c2ecf20Sopenharmony_ci msg.cmd = IPMI_GET_DEVICE_ID_CMD; 24198c2ecf20Sopenharmony_ci msg.data = NULL; 24208c2ecf20Sopenharmony_ci msg.data_len = 0; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci return i_ipmi_request(NULL, 24238c2ecf20Sopenharmony_ci intf, 24248c2ecf20Sopenharmony_ci (struct ipmi_addr *) &si, 24258c2ecf20Sopenharmony_ci 0, 24268c2ecf20Sopenharmony_ci &msg, 24278c2ecf20Sopenharmony_ci intf, 24288c2ecf20Sopenharmony_ci NULL, 24298c2ecf20Sopenharmony_ci NULL, 24308c2ecf20Sopenharmony_ci 0, 24318c2ecf20Sopenharmony_ci intf->addrinfo[0].address, 24328c2ecf20Sopenharmony_ci intf->addrinfo[0].lun, 24338c2ecf20Sopenharmony_ci -1, 0); 24348c2ecf20Sopenharmony_ci} 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_cistatic int __get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc) 24378c2ecf20Sopenharmony_ci{ 24388c2ecf20Sopenharmony_ci int rv; 24398c2ecf20Sopenharmony_ci unsigned int retry_count = 0; 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci intf->null_user_handler = bmc_device_id_handler; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ciretry: 24448c2ecf20Sopenharmony_ci bmc->cc = 0; 24458c2ecf20Sopenharmony_ci bmc->dyn_id_set = 2; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci rv = send_get_device_id_cmd(intf); 24488c2ecf20Sopenharmony_ci if (rv) 24498c2ecf20Sopenharmony_ci goto out_reset_handler; 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci wait_event(intf->waitq, bmc->dyn_id_set != 2); 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci if (!bmc->dyn_id_set) { 24548c2ecf20Sopenharmony_ci if ((bmc->cc == IPMI_DEVICE_IN_FW_UPDATE_ERR 24558c2ecf20Sopenharmony_ci || bmc->cc == IPMI_DEVICE_IN_INIT_ERR 24568c2ecf20Sopenharmony_ci || bmc->cc == IPMI_NOT_IN_MY_STATE_ERR) 24578c2ecf20Sopenharmony_ci && ++retry_count <= GET_DEVICE_ID_MAX_RETRY) { 24588c2ecf20Sopenharmony_ci msleep(500); 24598c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 24608c2ecf20Sopenharmony_ci "BMC returned 0x%2.2x, retry get bmc device id\n", 24618c2ecf20Sopenharmony_ci bmc->cc); 24628c2ecf20Sopenharmony_ci goto retry; 24638c2ecf20Sopenharmony_ci } 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci rv = -EIO; /* Something went wrong in the fetch. */ 24668c2ecf20Sopenharmony_ci } 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci /* dyn_id_set makes the id data available. */ 24698c2ecf20Sopenharmony_ci smp_rmb(); 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ciout_reset_handler: 24728c2ecf20Sopenharmony_ci intf->null_user_handler = NULL; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci return rv; 24758c2ecf20Sopenharmony_ci} 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci/* 24788c2ecf20Sopenharmony_ci * Fetch the device id for the bmc/interface. You must pass in either 24798c2ecf20Sopenharmony_ci * bmc or intf, this code will get the other one. If the data has 24808c2ecf20Sopenharmony_ci * been recently fetched, this will just use the cached data. Otherwise 24818c2ecf20Sopenharmony_ci * it will run a new fetch. 24828c2ecf20Sopenharmony_ci * 24838c2ecf20Sopenharmony_ci * Except for the first time this is called (in ipmi_add_smi()), 24848c2ecf20Sopenharmony_ci * this will always return good data; 24858c2ecf20Sopenharmony_ci */ 24868c2ecf20Sopenharmony_cistatic int __bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, 24878c2ecf20Sopenharmony_ci struct ipmi_device_id *id, 24888c2ecf20Sopenharmony_ci bool *guid_set, guid_t *guid, int intf_num) 24898c2ecf20Sopenharmony_ci{ 24908c2ecf20Sopenharmony_ci int rv = 0; 24918c2ecf20Sopenharmony_ci int prev_dyn_id_set, prev_guid_set; 24928c2ecf20Sopenharmony_ci bool intf_set = intf != NULL; 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci if (!intf) { 24958c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 24968c2ecf20Sopenharmony_ciretry_bmc_lock: 24978c2ecf20Sopenharmony_ci if (list_empty(&bmc->intfs)) { 24988c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 24998c2ecf20Sopenharmony_ci return -ENOENT; 25008c2ecf20Sopenharmony_ci } 25018c2ecf20Sopenharmony_ci intf = list_first_entry(&bmc->intfs, struct ipmi_smi, 25028c2ecf20Sopenharmony_ci bmc_link); 25038c2ecf20Sopenharmony_ci kref_get(&intf->refcount); 25048c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 25058c2ecf20Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 25068c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 25078c2ecf20Sopenharmony_ci if (intf != list_first_entry(&bmc->intfs, struct ipmi_smi, 25088c2ecf20Sopenharmony_ci bmc_link)) { 25098c2ecf20Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 25108c2ecf20Sopenharmony_ci kref_put(&intf->refcount, intf_free); 25118c2ecf20Sopenharmony_ci goto retry_bmc_lock; 25128c2ecf20Sopenharmony_ci } 25138c2ecf20Sopenharmony_ci } else { 25148c2ecf20Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 25158c2ecf20Sopenharmony_ci bmc = intf->bmc; 25168c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 25178c2ecf20Sopenharmony_ci kref_get(&intf->refcount); 25188c2ecf20Sopenharmony_ci } 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci /* If we have a valid and current ID, just return that. */ 25218c2ecf20Sopenharmony_ci if (intf->in_bmc_register || 25228c2ecf20Sopenharmony_ci (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry))) 25238c2ecf20Sopenharmony_ci goto out_noprocessing; 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci prev_guid_set = bmc->dyn_guid_set; 25268c2ecf20Sopenharmony_ci __get_guid(intf); 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci prev_dyn_id_set = bmc->dyn_id_set; 25298c2ecf20Sopenharmony_ci rv = __get_device_id(intf, bmc); 25308c2ecf20Sopenharmony_ci if (rv) 25318c2ecf20Sopenharmony_ci goto out; 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci /* 25348c2ecf20Sopenharmony_ci * The guid, device id, manufacturer id, and product id should 25358c2ecf20Sopenharmony_ci * not change on a BMC. If it does we have to do some dancing. 25368c2ecf20Sopenharmony_ci */ 25378c2ecf20Sopenharmony_ci if (!intf->bmc_registered 25388c2ecf20Sopenharmony_ci || (!prev_guid_set && bmc->dyn_guid_set) 25398c2ecf20Sopenharmony_ci || (!prev_dyn_id_set && bmc->dyn_id_set) 25408c2ecf20Sopenharmony_ci || (prev_guid_set && bmc->dyn_guid_set 25418c2ecf20Sopenharmony_ci && !guid_equal(&bmc->guid, &bmc->fetch_guid)) 25428c2ecf20Sopenharmony_ci || bmc->id.device_id != bmc->fetch_id.device_id 25438c2ecf20Sopenharmony_ci || bmc->id.manufacturer_id != bmc->fetch_id.manufacturer_id 25448c2ecf20Sopenharmony_ci || bmc->id.product_id != bmc->fetch_id.product_id) { 25458c2ecf20Sopenharmony_ci struct ipmi_device_id id = bmc->fetch_id; 25468c2ecf20Sopenharmony_ci int guid_set = bmc->dyn_guid_set; 25478c2ecf20Sopenharmony_ci guid_t guid; 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci guid = bmc->fetch_guid; 25508c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci __ipmi_bmc_unregister(intf); 25538c2ecf20Sopenharmony_ci /* Fill in the temporary BMC for good measure. */ 25548c2ecf20Sopenharmony_ci intf->bmc->id = id; 25558c2ecf20Sopenharmony_ci intf->bmc->dyn_guid_set = guid_set; 25568c2ecf20Sopenharmony_ci intf->bmc->guid = guid; 25578c2ecf20Sopenharmony_ci if (__ipmi_bmc_register(intf, &id, guid_set, &guid, intf_num)) 25588c2ecf20Sopenharmony_ci need_waiter(intf); /* Retry later on an error. */ 25598c2ecf20Sopenharmony_ci else 25608c2ecf20Sopenharmony_ci __scan_channels(intf, &id); 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci if (!intf_set) { 25648c2ecf20Sopenharmony_ci /* 25658c2ecf20Sopenharmony_ci * We weren't given the interface on the 25668c2ecf20Sopenharmony_ci * command line, so restart the operation on 25678c2ecf20Sopenharmony_ci * the next interface for the BMC. 25688c2ecf20Sopenharmony_ci */ 25698c2ecf20Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 25708c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 25718c2ecf20Sopenharmony_ci goto retry_bmc_lock; 25728c2ecf20Sopenharmony_ci } 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci /* We have a new BMC, set it up. */ 25758c2ecf20Sopenharmony_ci bmc = intf->bmc; 25768c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 25778c2ecf20Sopenharmony_ci goto out_noprocessing; 25788c2ecf20Sopenharmony_ci } else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id))) 25798c2ecf20Sopenharmony_ci /* Version info changes, scan the channels again. */ 25808c2ecf20Sopenharmony_ci __scan_channels(intf, &bmc->fetch_id); 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY; 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ciout: 25858c2ecf20Sopenharmony_ci if (rv && prev_dyn_id_set) { 25868c2ecf20Sopenharmony_ci rv = 0; /* Ignore failures if we have previous data. */ 25878c2ecf20Sopenharmony_ci bmc->dyn_id_set = prev_dyn_id_set; 25888c2ecf20Sopenharmony_ci } 25898c2ecf20Sopenharmony_ci if (!rv) { 25908c2ecf20Sopenharmony_ci bmc->id = bmc->fetch_id; 25918c2ecf20Sopenharmony_ci if (bmc->dyn_guid_set) 25928c2ecf20Sopenharmony_ci bmc->guid = bmc->fetch_guid; 25938c2ecf20Sopenharmony_ci else if (prev_guid_set) 25948c2ecf20Sopenharmony_ci /* 25958c2ecf20Sopenharmony_ci * The guid used to be valid and it failed to fetch, 25968c2ecf20Sopenharmony_ci * just use the cached value. 25978c2ecf20Sopenharmony_ci */ 25988c2ecf20Sopenharmony_ci bmc->dyn_guid_set = prev_guid_set; 25998c2ecf20Sopenharmony_ci } 26008c2ecf20Sopenharmony_ciout_noprocessing: 26018c2ecf20Sopenharmony_ci if (!rv) { 26028c2ecf20Sopenharmony_ci if (id) 26038c2ecf20Sopenharmony_ci *id = bmc->id; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci if (guid_set) 26068c2ecf20Sopenharmony_ci *guid_set = bmc->dyn_guid_set; 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci if (guid && bmc->dyn_guid_set) 26098c2ecf20Sopenharmony_ci *guid = bmc->guid; 26108c2ecf20Sopenharmony_ci } 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 26138c2ecf20Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci kref_put(&intf->refcount, intf_free); 26168c2ecf20Sopenharmony_ci return rv; 26178c2ecf20Sopenharmony_ci} 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_cistatic int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, 26208c2ecf20Sopenharmony_ci struct ipmi_device_id *id, 26218c2ecf20Sopenharmony_ci bool *guid_set, guid_t *guid) 26228c2ecf20Sopenharmony_ci{ 26238c2ecf20Sopenharmony_ci return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1); 26248c2ecf20Sopenharmony_ci} 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_cistatic ssize_t device_id_show(struct device *dev, 26278c2ecf20Sopenharmony_ci struct device_attribute *attr, 26288c2ecf20Sopenharmony_ci char *buf) 26298c2ecf20Sopenharmony_ci{ 26308c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 26318c2ecf20Sopenharmony_ci struct ipmi_device_id id; 26328c2ecf20Sopenharmony_ci int rv; 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 26358c2ecf20Sopenharmony_ci if (rv) 26368c2ecf20Sopenharmony_ci return rv; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci return snprintf(buf, 10, "%u\n", id.device_id); 26398c2ecf20Sopenharmony_ci} 26408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(device_id); 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_cistatic ssize_t provides_device_sdrs_show(struct device *dev, 26438c2ecf20Sopenharmony_ci struct device_attribute *attr, 26448c2ecf20Sopenharmony_ci char *buf) 26458c2ecf20Sopenharmony_ci{ 26468c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 26478c2ecf20Sopenharmony_ci struct ipmi_device_id id; 26488c2ecf20Sopenharmony_ci int rv; 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 26518c2ecf20Sopenharmony_ci if (rv) 26528c2ecf20Sopenharmony_ci return rv; 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci return snprintf(buf, 10, "%u\n", (id.device_revision & 0x80) >> 7); 26558c2ecf20Sopenharmony_ci} 26568c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(provides_device_sdrs); 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_cistatic ssize_t revision_show(struct device *dev, struct device_attribute *attr, 26598c2ecf20Sopenharmony_ci char *buf) 26608c2ecf20Sopenharmony_ci{ 26618c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 26628c2ecf20Sopenharmony_ci struct ipmi_device_id id; 26638c2ecf20Sopenharmony_ci int rv; 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 26668c2ecf20Sopenharmony_ci if (rv) 26678c2ecf20Sopenharmony_ci return rv; 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_ci return snprintf(buf, 20, "%u\n", id.device_revision & 0x0F); 26708c2ecf20Sopenharmony_ci} 26718c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(revision); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_cistatic ssize_t firmware_revision_show(struct device *dev, 26748c2ecf20Sopenharmony_ci struct device_attribute *attr, 26758c2ecf20Sopenharmony_ci char *buf) 26768c2ecf20Sopenharmony_ci{ 26778c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 26788c2ecf20Sopenharmony_ci struct ipmi_device_id id; 26798c2ecf20Sopenharmony_ci int rv; 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 26828c2ecf20Sopenharmony_ci if (rv) 26838c2ecf20Sopenharmony_ci return rv; 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci return snprintf(buf, 20, "%u.%x\n", id.firmware_revision_1, 26868c2ecf20Sopenharmony_ci id.firmware_revision_2); 26878c2ecf20Sopenharmony_ci} 26888c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_revision); 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_cistatic ssize_t ipmi_version_show(struct device *dev, 26918c2ecf20Sopenharmony_ci struct device_attribute *attr, 26928c2ecf20Sopenharmony_ci char *buf) 26938c2ecf20Sopenharmony_ci{ 26948c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 26958c2ecf20Sopenharmony_ci struct ipmi_device_id id; 26968c2ecf20Sopenharmony_ci int rv; 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 26998c2ecf20Sopenharmony_ci if (rv) 27008c2ecf20Sopenharmony_ci return rv; 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_ci return snprintf(buf, 20, "%u.%u\n", 27038c2ecf20Sopenharmony_ci ipmi_version_major(&id), 27048c2ecf20Sopenharmony_ci ipmi_version_minor(&id)); 27058c2ecf20Sopenharmony_ci} 27068c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(ipmi_version); 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_cistatic ssize_t add_dev_support_show(struct device *dev, 27098c2ecf20Sopenharmony_ci struct device_attribute *attr, 27108c2ecf20Sopenharmony_ci char *buf) 27118c2ecf20Sopenharmony_ci{ 27128c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 27138c2ecf20Sopenharmony_ci struct ipmi_device_id id; 27148c2ecf20Sopenharmony_ci int rv; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 27178c2ecf20Sopenharmony_ci if (rv) 27188c2ecf20Sopenharmony_ci return rv; 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci return snprintf(buf, 10, "0x%02x\n", id.additional_device_support); 27218c2ecf20Sopenharmony_ci} 27228c2ecf20Sopenharmony_cistatic DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show, 27238c2ecf20Sopenharmony_ci NULL); 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_cistatic ssize_t manufacturer_id_show(struct device *dev, 27268c2ecf20Sopenharmony_ci struct device_attribute *attr, 27278c2ecf20Sopenharmony_ci char *buf) 27288c2ecf20Sopenharmony_ci{ 27298c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 27308c2ecf20Sopenharmony_ci struct ipmi_device_id id; 27318c2ecf20Sopenharmony_ci int rv; 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 27348c2ecf20Sopenharmony_ci if (rv) 27358c2ecf20Sopenharmony_ci return rv; 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci return snprintf(buf, 20, "0x%6.6x\n", id.manufacturer_id); 27388c2ecf20Sopenharmony_ci} 27398c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(manufacturer_id); 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_cistatic ssize_t product_id_show(struct device *dev, 27428c2ecf20Sopenharmony_ci struct device_attribute *attr, 27438c2ecf20Sopenharmony_ci char *buf) 27448c2ecf20Sopenharmony_ci{ 27458c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 27468c2ecf20Sopenharmony_ci struct ipmi_device_id id; 27478c2ecf20Sopenharmony_ci int rv; 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 27508c2ecf20Sopenharmony_ci if (rv) 27518c2ecf20Sopenharmony_ci return rv; 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci return snprintf(buf, 10, "0x%4.4x\n", id.product_id); 27548c2ecf20Sopenharmony_ci} 27558c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(product_id); 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_cistatic ssize_t aux_firmware_rev_show(struct device *dev, 27588c2ecf20Sopenharmony_ci struct device_attribute *attr, 27598c2ecf20Sopenharmony_ci char *buf) 27608c2ecf20Sopenharmony_ci{ 27618c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 27628c2ecf20Sopenharmony_ci struct ipmi_device_id id; 27638c2ecf20Sopenharmony_ci int rv; 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 27668c2ecf20Sopenharmony_ci if (rv) 27678c2ecf20Sopenharmony_ci return rv; 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n", 27708c2ecf20Sopenharmony_ci id.aux_firmware_revision[3], 27718c2ecf20Sopenharmony_ci id.aux_firmware_revision[2], 27728c2ecf20Sopenharmony_ci id.aux_firmware_revision[1], 27738c2ecf20Sopenharmony_ci id.aux_firmware_revision[0]); 27748c2ecf20Sopenharmony_ci} 27758c2ecf20Sopenharmony_cistatic DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL); 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_cistatic ssize_t guid_show(struct device *dev, struct device_attribute *attr, 27788c2ecf20Sopenharmony_ci char *buf) 27798c2ecf20Sopenharmony_ci{ 27808c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 27818c2ecf20Sopenharmony_ci bool guid_set; 27828c2ecf20Sopenharmony_ci guid_t guid; 27838c2ecf20Sopenharmony_ci int rv; 27848c2ecf20Sopenharmony_ci 27858c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, &guid); 27868c2ecf20Sopenharmony_ci if (rv) 27878c2ecf20Sopenharmony_ci return rv; 27888c2ecf20Sopenharmony_ci if (!guid_set) 27898c2ecf20Sopenharmony_ci return -ENOENT; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci return snprintf(buf, UUID_STRING_LEN + 1 + 1, "%pUl\n", &guid); 27928c2ecf20Sopenharmony_ci} 27938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(guid); 27948c2ecf20Sopenharmony_ci 27958c2ecf20Sopenharmony_cistatic struct attribute *bmc_dev_attrs[] = { 27968c2ecf20Sopenharmony_ci &dev_attr_device_id.attr, 27978c2ecf20Sopenharmony_ci &dev_attr_provides_device_sdrs.attr, 27988c2ecf20Sopenharmony_ci &dev_attr_revision.attr, 27998c2ecf20Sopenharmony_ci &dev_attr_firmware_revision.attr, 28008c2ecf20Sopenharmony_ci &dev_attr_ipmi_version.attr, 28018c2ecf20Sopenharmony_ci &dev_attr_additional_device_support.attr, 28028c2ecf20Sopenharmony_ci &dev_attr_manufacturer_id.attr, 28038c2ecf20Sopenharmony_ci &dev_attr_product_id.attr, 28048c2ecf20Sopenharmony_ci &dev_attr_aux_firmware_revision.attr, 28058c2ecf20Sopenharmony_ci &dev_attr_guid.attr, 28068c2ecf20Sopenharmony_ci NULL 28078c2ecf20Sopenharmony_ci}; 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_cistatic umode_t bmc_dev_attr_is_visible(struct kobject *kobj, 28108c2ecf20Sopenharmony_ci struct attribute *attr, int idx) 28118c2ecf20Sopenharmony_ci{ 28128c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 28138c2ecf20Sopenharmony_ci struct bmc_device *bmc = to_bmc_device(dev); 28148c2ecf20Sopenharmony_ci umode_t mode = attr->mode; 28158c2ecf20Sopenharmony_ci int rv; 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci if (attr == &dev_attr_aux_firmware_revision.attr) { 28188c2ecf20Sopenharmony_ci struct ipmi_device_id id; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); 28218c2ecf20Sopenharmony_ci return (!rv && id.aux_firmware_revision_set) ? mode : 0; 28228c2ecf20Sopenharmony_ci } 28238c2ecf20Sopenharmony_ci if (attr == &dev_attr_guid.attr) { 28248c2ecf20Sopenharmony_ci bool guid_set; 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, NULL); 28278c2ecf20Sopenharmony_ci return (!rv && guid_set) ? mode : 0; 28288c2ecf20Sopenharmony_ci } 28298c2ecf20Sopenharmony_ci return mode; 28308c2ecf20Sopenharmony_ci} 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_cistatic const struct attribute_group bmc_dev_attr_group = { 28338c2ecf20Sopenharmony_ci .attrs = bmc_dev_attrs, 28348c2ecf20Sopenharmony_ci .is_visible = bmc_dev_attr_is_visible, 28358c2ecf20Sopenharmony_ci}; 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_cistatic const struct attribute_group *bmc_dev_attr_groups[] = { 28388c2ecf20Sopenharmony_ci &bmc_dev_attr_group, 28398c2ecf20Sopenharmony_ci NULL 28408c2ecf20Sopenharmony_ci}; 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_cistatic const struct device_type bmc_device_type = { 28438c2ecf20Sopenharmony_ci .groups = bmc_dev_attr_groups, 28448c2ecf20Sopenharmony_ci}; 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_cistatic int __find_bmc_guid(struct device *dev, const void *data) 28478c2ecf20Sopenharmony_ci{ 28488c2ecf20Sopenharmony_ci const guid_t *guid = data; 28498c2ecf20Sopenharmony_ci struct bmc_device *bmc; 28508c2ecf20Sopenharmony_ci int rv; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci if (dev->type != &bmc_device_type) 28538c2ecf20Sopenharmony_ci return 0; 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci bmc = to_bmc_device(dev); 28568c2ecf20Sopenharmony_ci rv = bmc->dyn_guid_set && guid_equal(&bmc->guid, guid); 28578c2ecf20Sopenharmony_ci if (rv) 28588c2ecf20Sopenharmony_ci rv = kref_get_unless_zero(&bmc->usecount); 28598c2ecf20Sopenharmony_ci return rv; 28608c2ecf20Sopenharmony_ci} 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci/* 28638c2ecf20Sopenharmony_ci * Returns with the bmc's usecount incremented, if it is non-NULL. 28648c2ecf20Sopenharmony_ci */ 28658c2ecf20Sopenharmony_cistatic struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv, 28668c2ecf20Sopenharmony_ci guid_t *guid) 28678c2ecf20Sopenharmony_ci{ 28688c2ecf20Sopenharmony_ci struct device *dev; 28698c2ecf20Sopenharmony_ci struct bmc_device *bmc = NULL; 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci dev = driver_find_device(drv, NULL, guid, __find_bmc_guid); 28728c2ecf20Sopenharmony_ci if (dev) { 28738c2ecf20Sopenharmony_ci bmc = to_bmc_device(dev); 28748c2ecf20Sopenharmony_ci put_device(dev); 28758c2ecf20Sopenharmony_ci } 28768c2ecf20Sopenharmony_ci return bmc; 28778c2ecf20Sopenharmony_ci} 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_cistruct prod_dev_id { 28808c2ecf20Sopenharmony_ci unsigned int product_id; 28818c2ecf20Sopenharmony_ci unsigned char device_id; 28828c2ecf20Sopenharmony_ci}; 28838c2ecf20Sopenharmony_ci 28848c2ecf20Sopenharmony_cistatic int __find_bmc_prod_dev_id(struct device *dev, const void *data) 28858c2ecf20Sopenharmony_ci{ 28868c2ecf20Sopenharmony_ci const struct prod_dev_id *cid = data; 28878c2ecf20Sopenharmony_ci struct bmc_device *bmc; 28888c2ecf20Sopenharmony_ci int rv; 28898c2ecf20Sopenharmony_ci 28908c2ecf20Sopenharmony_ci if (dev->type != &bmc_device_type) 28918c2ecf20Sopenharmony_ci return 0; 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci bmc = to_bmc_device(dev); 28948c2ecf20Sopenharmony_ci rv = (bmc->id.product_id == cid->product_id 28958c2ecf20Sopenharmony_ci && bmc->id.device_id == cid->device_id); 28968c2ecf20Sopenharmony_ci if (rv) 28978c2ecf20Sopenharmony_ci rv = kref_get_unless_zero(&bmc->usecount); 28988c2ecf20Sopenharmony_ci return rv; 28998c2ecf20Sopenharmony_ci} 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci/* 29028c2ecf20Sopenharmony_ci * Returns with the bmc's usecount incremented, if it is non-NULL. 29038c2ecf20Sopenharmony_ci */ 29048c2ecf20Sopenharmony_cistatic struct bmc_device *ipmi_find_bmc_prod_dev_id( 29058c2ecf20Sopenharmony_ci struct device_driver *drv, 29068c2ecf20Sopenharmony_ci unsigned int product_id, unsigned char device_id) 29078c2ecf20Sopenharmony_ci{ 29088c2ecf20Sopenharmony_ci struct prod_dev_id id = { 29098c2ecf20Sopenharmony_ci .product_id = product_id, 29108c2ecf20Sopenharmony_ci .device_id = device_id, 29118c2ecf20Sopenharmony_ci }; 29128c2ecf20Sopenharmony_ci struct device *dev; 29138c2ecf20Sopenharmony_ci struct bmc_device *bmc = NULL; 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id); 29168c2ecf20Sopenharmony_ci if (dev) { 29178c2ecf20Sopenharmony_ci bmc = to_bmc_device(dev); 29188c2ecf20Sopenharmony_ci put_device(dev); 29198c2ecf20Sopenharmony_ci } 29208c2ecf20Sopenharmony_ci return bmc; 29218c2ecf20Sopenharmony_ci} 29228c2ecf20Sopenharmony_ci 29238c2ecf20Sopenharmony_cistatic DEFINE_IDA(ipmi_bmc_ida); 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_cistatic void 29268c2ecf20Sopenharmony_cirelease_bmc_device(struct device *dev) 29278c2ecf20Sopenharmony_ci{ 29288c2ecf20Sopenharmony_ci kfree(to_bmc_device(dev)); 29298c2ecf20Sopenharmony_ci} 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_cistatic void cleanup_bmc_work(struct work_struct *work) 29328c2ecf20Sopenharmony_ci{ 29338c2ecf20Sopenharmony_ci struct bmc_device *bmc = container_of(work, struct bmc_device, 29348c2ecf20Sopenharmony_ci remove_work); 29358c2ecf20Sopenharmony_ci int id = bmc->pdev.id; /* Unregister overwrites id */ 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci platform_device_unregister(&bmc->pdev); 29388c2ecf20Sopenharmony_ci ida_simple_remove(&ipmi_bmc_ida, id); 29398c2ecf20Sopenharmony_ci} 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_cistatic void 29428c2ecf20Sopenharmony_cicleanup_bmc_device(struct kref *ref) 29438c2ecf20Sopenharmony_ci{ 29448c2ecf20Sopenharmony_ci struct bmc_device *bmc = container_of(ref, struct bmc_device, usecount); 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci /* 29478c2ecf20Sopenharmony_ci * Remove the platform device in a work queue to avoid issues 29488c2ecf20Sopenharmony_ci * with removing the device attributes while reading a device 29498c2ecf20Sopenharmony_ci * attribute. 29508c2ecf20Sopenharmony_ci */ 29518c2ecf20Sopenharmony_ci queue_work(remove_work_wq, &bmc->remove_work); 29528c2ecf20Sopenharmony_ci} 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci/* 29558c2ecf20Sopenharmony_ci * Must be called with intf->bmc_reg_mutex held. 29568c2ecf20Sopenharmony_ci */ 29578c2ecf20Sopenharmony_cistatic void __ipmi_bmc_unregister(struct ipmi_smi *intf) 29588c2ecf20Sopenharmony_ci{ 29598c2ecf20Sopenharmony_ci struct bmc_device *bmc = intf->bmc; 29608c2ecf20Sopenharmony_ci 29618c2ecf20Sopenharmony_ci if (!intf->bmc_registered) 29628c2ecf20Sopenharmony_ci return; 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci sysfs_remove_link(&intf->si_dev->kobj, "bmc"); 29658c2ecf20Sopenharmony_ci sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name); 29668c2ecf20Sopenharmony_ci kfree(intf->my_dev_name); 29678c2ecf20Sopenharmony_ci intf->my_dev_name = NULL; 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 29708c2ecf20Sopenharmony_ci list_del(&intf->bmc_link); 29718c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 29728c2ecf20Sopenharmony_ci intf->bmc = &intf->tmp_bmc; 29738c2ecf20Sopenharmony_ci kref_put(&bmc->usecount, cleanup_bmc_device); 29748c2ecf20Sopenharmony_ci intf->bmc_registered = false; 29758c2ecf20Sopenharmony_ci} 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_cistatic void ipmi_bmc_unregister(struct ipmi_smi *intf) 29788c2ecf20Sopenharmony_ci{ 29798c2ecf20Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 29808c2ecf20Sopenharmony_ci __ipmi_bmc_unregister(intf); 29818c2ecf20Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 29828c2ecf20Sopenharmony_ci} 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci/* 29858c2ecf20Sopenharmony_ci * Must be called with intf->bmc_reg_mutex held. 29868c2ecf20Sopenharmony_ci */ 29878c2ecf20Sopenharmony_cistatic int __ipmi_bmc_register(struct ipmi_smi *intf, 29888c2ecf20Sopenharmony_ci struct ipmi_device_id *id, 29898c2ecf20Sopenharmony_ci bool guid_set, guid_t *guid, int intf_num) 29908c2ecf20Sopenharmony_ci{ 29918c2ecf20Sopenharmony_ci int rv; 29928c2ecf20Sopenharmony_ci struct bmc_device *bmc; 29938c2ecf20Sopenharmony_ci struct bmc_device *old_bmc; 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci /* 29968c2ecf20Sopenharmony_ci * platform_device_register() can cause bmc_reg_mutex to 29978c2ecf20Sopenharmony_ci * be claimed because of the is_visible functions of 29988c2ecf20Sopenharmony_ci * the attributes. Eliminate possible recursion and 29998c2ecf20Sopenharmony_ci * release the lock. 30008c2ecf20Sopenharmony_ci */ 30018c2ecf20Sopenharmony_ci intf->in_bmc_register = true; 30028c2ecf20Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_ci /* 30058c2ecf20Sopenharmony_ci * Try to find if there is an bmc_device struct 30068c2ecf20Sopenharmony_ci * representing the interfaced BMC already 30078c2ecf20Sopenharmony_ci */ 30088c2ecf20Sopenharmony_ci mutex_lock(&ipmidriver_mutex); 30098c2ecf20Sopenharmony_ci if (guid_set) 30108c2ecf20Sopenharmony_ci old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, guid); 30118c2ecf20Sopenharmony_ci else 30128c2ecf20Sopenharmony_ci old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, 30138c2ecf20Sopenharmony_ci id->product_id, 30148c2ecf20Sopenharmony_ci id->device_id); 30158c2ecf20Sopenharmony_ci 30168c2ecf20Sopenharmony_ci /* 30178c2ecf20Sopenharmony_ci * If there is already an bmc_device, free the new one, 30188c2ecf20Sopenharmony_ci * otherwise register the new BMC device 30198c2ecf20Sopenharmony_ci */ 30208c2ecf20Sopenharmony_ci if (old_bmc) { 30218c2ecf20Sopenharmony_ci bmc = old_bmc; 30228c2ecf20Sopenharmony_ci /* 30238c2ecf20Sopenharmony_ci * Note: old_bmc already has usecount incremented by 30248c2ecf20Sopenharmony_ci * the BMC find functions. 30258c2ecf20Sopenharmony_ci */ 30268c2ecf20Sopenharmony_ci intf->bmc = old_bmc; 30278c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 30288c2ecf20Sopenharmony_ci list_add_tail(&intf->bmc_link, &bmc->intfs); 30298c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci dev_info(intf->si_dev, 30328c2ecf20Sopenharmony_ci "interfacing existing BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", 30338c2ecf20Sopenharmony_ci bmc->id.manufacturer_id, 30348c2ecf20Sopenharmony_ci bmc->id.product_id, 30358c2ecf20Sopenharmony_ci bmc->id.device_id); 30368c2ecf20Sopenharmony_ci } else { 30378c2ecf20Sopenharmony_ci bmc = kzalloc(sizeof(*bmc), GFP_KERNEL); 30388c2ecf20Sopenharmony_ci if (!bmc) { 30398c2ecf20Sopenharmony_ci rv = -ENOMEM; 30408c2ecf20Sopenharmony_ci goto out; 30418c2ecf20Sopenharmony_ci } 30428c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bmc->intfs); 30438c2ecf20Sopenharmony_ci mutex_init(&bmc->dyn_mutex); 30448c2ecf20Sopenharmony_ci INIT_WORK(&bmc->remove_work, cleanup_bmc_work); 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci bmc->id = *id; 30478c2ecf20Sopenharmony_ci bmc->dyn_id_set = 1; 30488c2ecf20Sopenharmony_ci bmc->dyn_guid_set = guid_set; 30498c2ecf20Sopenharmony_ci bmc->guid = *guid; 30508c2ecf20Sopenharmony_ci bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY; 30518c2ecf20Sopenharmony_ci 30528c2ecf20Sopenharmony_ci bmc->pdev.name = "ipmi_bmc"; 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL); 30558c2ecf20Sopenharmony_ci if (rv < 0) { 30568c2ecf20Sopenharmony_ci kfree(bmc); 30578c2ecf20Sopenharmony_ci goto out; 30588c2ecf20Sopenharmony_ci } 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_ci bmc->pdev.dev.driver = &ipmidriver.driver; 30618c2ecf20Sopenharmony_ci bmc->pdev.id = rv; 30628c2ecf20Sopenharmony_ci bmc->pdev.dev.release = release_bmc_device; 30638c2ecf20Sopenharmony_ci bmc->pdev.dev.type = &bmc_device_type; 30648c2ecf20Sopenharmony_ci kref_init(&bmc->usecount); 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci intf->bmc = bmc; 30678c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 30688c2ecf20Sopenharmony_ci list_add_tail(&intf->bmc_link, &bmc->intfs); 30698c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci rv = platform_device_register(&bmc->pdev); 30728c2ecf20Sopenharmony_ci if (rv) { 30738c2ecf20Sopenharmony_ci dev_err(intf->si_dev, 30748c2ecf20Sopenharmony_ci "Unable to register bmc device: %d\n", 30758c2ecf20Sopenharmony_ci rv); 30768c2ecf20Sopenharmony_ci goto out_list_del; 30778c2ecf20Sopenharmony_ci } 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci dev_info(intf->si_dev, 30808c2ecf20Sopenharmony_ci "Found new BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", 30818c2ecf20Sopenharmony_ci bmc->id.manufacturer_id, 30828c2ecf20Sopenharmony_ci bmc->id.product_id, 30838c2ecf20Sopenharmony_ci bmc->id.device_id); 30848c2ecf20Sopenharmony_ci } 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_ci /* 30878c2ecf20Sopenharmony_ci * create symlink from system interface device to bmc device 30888c2ecf20Sopenharmony_ci * and back. 30898c2ecf20Sopenharmony_ci */ 30908c2ecf20Sopenharmony_ci rv = sysfs_create_link(&intf->si_dev->kobj, &bmc->pdev.dev.kobj, "bmc"); 30918c2ecf20Sopenharmony_ci if (rv) { 30928c2ecf20Sopenharmony_ci dev_err(intf->si_dev, "Unable to create bmc symlink: %d\n", rv); 30938c2ecf20Sopenharmony_ci goto out_put_bmc; 30948c2ecf20Sopenharmony_ci } 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci if (intf_num == -1) 30978c2ecf20Sopenharmony_ci intf_num = intf->intf_num; 30988c2ecf20Sopenharmony_ci intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", intf_num); 30998c2ecf20Sopenharmony_ci if (!intf->my_dev_name) { 31008c2ecf20Sopenharmony_ci rv = -ENOMEM; 31018c2ecf20Sopenharmony_ci dev_err(intf->si_dev, "Unable to allocate link from BMC: %d\n", 31028c2ecf20Sopenharmony_ci rv); 31038c2ecf20Sopenharmony_ci goto out_unlink1; 31048c2ecf20Sopenharmony_ci } 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci rv = sysfs_create_link(&bmc->pdev.dev.kobj, &intf->si_dev->kobj, 31078c2ecf20Sopenharmony_ci intf->my_dev_name); 31088c2ecf20Sopenharmony_ci if (rv) { 31098c2ecf20Sopenharmony_ci dev_err(intf->si_dev, "Unable to create symlink to bmc: %d\n", 31108c2ecf20Sopenharmony_ci rv); 31118c2ecf20Sopenharmony_ci goto out_free_my_dev_name; 31128c2ecf20Sopenharmony_ci } 31138c2ecf20Sopenharmony_ci 31148c2ecf20Sopenharmony_ci intf->bmc_registered = true; 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ciout: 31178c2ecf20Sopenharmony_ci mutex_unlock(&ipmidriver_mutex); 31188c2ecf20Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 31198c2ecf20Sopenharmony_ci intf->in_bmc_register = false; 31208c2ecf20Sopenharmony_ci return rv; 31218c2ecf20Sopenharmony_ci 31228c2ecf20Sopenharmony_ci 31238c2ecf20Sopenharmony_ciout_free_my_dev_name: 31248c2ecf20Sopenharmony_ci kfree(intf->my_dev_name); 31258c2ecf20Sopenharmony_ci intf->my_dev_name = NULL; 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ciout_unlink1: 31288c2ecf20Sopenharmony_ci sysfs_remove_link(&intf->si_dev->kobj, "bmc"); 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ciout_put_bmc: 31318c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 31328c2ecf20Sopenharmony_ci list_del(&intf->bmc_link); 31338c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 31348c2ecf20Sopenharmony_ci intf->bmc = &intf->tmp_bmc; 31358c2ecf20Sopenharmony_ci kref_put(&bmc->usecount, cleanup_bmc_device); 31368c2ecf20Sopenharmony_ci goto out; 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ciout_list_del: 31398c2ecf20Sopenharmony_ci mutex_lock(&bmc->dyn_mutex); 31408c2ecf20Sopenharmony_ci list_del(&intf->bmc_link); 31418c2ecf20Sopenharmony_ci mutex_unlock(&bmc->dyn_mutex); 31428c2ecf20Sopenharmony_ci intf->bmc = &intf->tmp_bmc; 31438c2ecf20Sopenharmony_ci put_device(&bmc->pdev.dev); 31448c2ecf20Sopenharmony_ci goto out; 31458c2ecf20Sopenharmony_ci} 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_cistatic int 31488c2ecf20Sopenharmony_cisend_guid_cmd(struct ipmi_smi *intf, int chan) 31498c2ecf20Sopenharmony_ci{ 31508c2ecf20Sopenharmony_ci struct kernel_ipmi_msg msg; 31518c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr si; 31528c2ecf20Sopenharmony_ci 31538c2ecf20Sopenharmony_ci si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 31548c2ecf20Sopenharmony_ci si.channel = IPMI_BMC_CHANNEL; 31558c2ecf20Sopenharmony_ci si.lun = 0; 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci msg.netfn = IPMI_NETFN_APP_REQUEST; 31588c2ecf20Sopenharmony_ci msg.cmd = IPMI_GET_DEVICE_GUID_CMD; 31598c2ecf20Sopenharmony_ci msg.data = NULL; 31608c2ecf20Sopenharmony_ci msg.data_len = 0; 31618c2ecf20Sopenharmony_ci return i_ipmi_request(NULL, 31628c2ecf20Sopenharmony_ci intf, 31638c2ecf20Sopenharmony_ci (struct ipmi_addr *) &si, 31648c2ecf20Sopenharmony_ci 0, 31658c2ecf20Sopenharmony_ci &msg, 31668c2ecf20Sopenharmony_ci intf, 31678c2ecf20Sopenharmony_ci NULL, 31688c2ecf20Sopenharmony_ci NULL, 31698c2ecf20Sopenharmony_ci 0, 31708c2ecf20Sopenharmony_ci intf->addrinfo[0].address, 31718c2ecf20Sopenharmony_ci intf->addrinfo[0].lun, 31728c2ecf20Sopenharmony_ci -1, 0); 31738c2ecf20Sopenharmony_ci} 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_cistatic void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) 31768c2ecf20Sopenharmony_ci{ 31778c2ecf20Sopenharmony_ci struct bmc_device *bmc = intf->bmc; 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 31808c2ecf20Sopenharmony_ci || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) 31818c2ecf20Sopenharmony_ci || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD)) 31828c2ecf20Sopenharmony_ci /* Not for me */ 31838c2ecf20Sopenharmony_ci return; 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci if (msg->msg.data[0] != 0) { 31868c2ecf20Sopenharmony_ci /* Error from getting the GUID, the BMC doesn't have one. */ 31878c2ecf20Sopenharmony_ci bmc->dyn_guid_set = 0; 31888c2ecf20Sopenharmony_ci goto out; 31898c2ecf20Sopenharmony_ci } 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_ci if (msg->msg.data_len < UUID_SIZE + 1) { 31928c2ecf20Sopenharmony_ci bmc->dyn_guid_set = 0; 31938c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 31948c2ecf20Sopenharmony_ci "The GUID response from the BMC was too short, it was %d but should have been %d. Assuming GUID is not available.\n", 31958c2ecf20Sopenharmony_ci msg->msg.data_len, UUID_SIZE + 1); 31968c2ecf20Sopenharmony_ci goto out; 31978c2ecf20Sopenharmony_ci } 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci import_guid(&bmc->fetch_guid, msg->msg.data + 1); 32008c2ecf20Sopenharmony_ci /* 32018c2ecf20Sopenharmony_ci * Make sure the guid data is available before setting 32028c2ecf20Sopenharmony_ci * dyn_guid_set. 32038c2ecf20Sopenharmony_ci */ 32048c2ecf20Sopenharmony_ci smp_wmb(); 32058c2ecf20Sopenharmony_ci bmc->dyn_guid_set = 1; 32068c2ecf20Sopenharmony_ci out: 32078c2ecf20Sopenharmony_ci wake_up(&intf->waitq); 32088c2ecf20Sopenharmony_ci} 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_cistatic void __get_guid(struct ipmi_smi *intf) 32118c2ecf20Sopenharmony_ci{ 32128c2ecf20Sopenharmony_ci int rv; 32138c2ecf20Sopenharmony_ci struct bmc_device *bmc = intf->bmc; 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci bmc->dyn_guid_set = 2; 32168c2ecf20Sopenharmony_ci intf->null_user_handler = guid_handler; 32178c2ecf20Sopenharmony_ci rv = send_guid_cmd(intf, 0); 32188c2ecf20Sopenharmony_ci if (rv) 32198c2ecf20Sopenharmony_ci /* Send failed, no GUID available. */ 32208c2ecf20Sopenharmony_ci bmc->dyn_guid_set = 0; 32218c2ecf20Sopenharmony_ci else 32228c2ecf20Sopenharmony_ci wait_event(intf->waitq, bmc->dyn_guid_set != 2); 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci /* dyn_guid_set makes the guid data available. */ 32258c2ecf20Sopenharmony_ci smp_rmb(); 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci intf->null_user_handler = NULL; 32288c2ecf20Sopenharmony_ci} 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_cistatic int 32318c2ecf20Sopenharmony_cisend_channel_info_cmd(struct ipmi_smi *intf, int chan) 32328c2ecf20Sopenharmony_ci{ 32338c2ecf20Sopenharmony_ci struct kernel_ipmi_msg msg; 32348c2ecf20Sopenharmony_ci unsigned char data[1]; 32358c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr si; 32368c2ecf20Sopenharmony_ci 32378c2ecf20Sopenharmony_ci si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 32388c2ecf20Sopenharmony_ci si.channel = IPMI_BMC_CHANNEL; 32398c2ecf20Sopenharmony_ci si.lun = 0; 32408c2ecf20Sopenharmony_ci 32418c2ecf20Sopenharmony_ci msg.netfn = IPMI_NETFN_APP_REQUEST; 32428c2ecf20Sopenharmony_ci msg.cmd = IPMI_GET_CHANNEL_INFO_CMD; 32438c2ecf20Sopenharmony_ci msg.data = data; 32448c2ecf20Sopenharmony_ci msg.data_len = 1; 32458c2ecf20Sopenharmony_ci data[0] = chan; 32468c2ecf20Sopenharmony_ci return i_ipmi_request(NULL, 32478c2ecf20Sopenharmony_ci intf, 32488c2ecf20Sopenharmony_ci (struct ipmi_addr *) &si, 32498c2ecf20Sopenharmony_ci 0, 32508c2ecf20Sopenharmony_ci &msg, 32518c2ecf20Sopenharmony_ci intf, 32528c2ecf20Sopenharmony_ci NULL, 32538c2ecf20Sopenharmony_ci NULL, 32548c2ecf20Sopenharmony_ci 0, 32558c2ecf20Sopenharmony_ci intf->addrinfo[0].address, 32568c2ecf20Sopenharmony_ci intf->addrinfo[0].lun, 32578c2ecf20Sopenharmony_ci -1, 0); 32588c2ecf20Sopenharmony_ci} 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_cistatic void 32618c2ecf20Sopenharmony_cichannel_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) 32628c2ecf20Sopenharmony_ci{ 32638c2ecf20Sopenharmony_ci int rv = 0; 32648c2ecf20Sopenharmony_ci int ch; 32658c2ecf20Sopenharmony_ci unsigned int set = intf->curr_working_cset; 32668c2ecf20Sopenharmony_ci struct ipmi_channel *chans; 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 32698c2ecf20Sopenharmony_ci && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) 32708c2ecf20Sopenharmony_ci && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD)) { 32718c2ecf20Sopenharmony_ci /* It's the one we want */ 32728c2ecf20Sopenharmony_ci if (msg->msg.data[0] != 0) { 32738c2ecf20Sopenharmony_ci /* Got an error from the channel, just go on. */ 32748c2ecf20Sopenharmony_ci if (msg->msg.data[0] == IPMI_INVALID_COMMAND_ERR) { 32758c2ecf20Sopenharmony_ci /* 32768c2ecf20Sopenharmony_ci * If the MC does not support this 32778c2ecf20Sopenharmony_ci * command, that is legal. We just 32788c2ecf20Sopenharmony_ci * assume it has one IPMB at channel 32798c2ecf20Sopenharmony_ci * zero. 32808c2ecf20Sopenharmony_ci */ 32818c2ecf20Sopenharmony_ci intf->wchannels[set].c[0].medium 32828c2ecf20Sopenharmony_ci = IPMI_CHANNEL_MEDIUM_IPMB; 32838c2ecf20Sopenharmony_ci intf->wchannels[set].c[0].protocol 32848c2ecf20Sopenharmony_ci = IPMI_CHANNEL_PROTOCOL_IPMB; 32858c2ecf20Sopenharmony_ci 32868c2ecf20Sopenharmony_ci intf->channel_list = intf->wchannels + set; 32878c2ecf20Sopenharmony_ci intf->channels_ready = true; 32888c2ecf20Sopenharmony_ci wake_up(&intf->waitq); 32898c2ecf20Sopenharmony_ci goto out; 32908c2ecf20Sopenharmony_ci } 32918c2ecf20Sopenharmony_ci goto next_channel; 32928c2ecf20Sopenharmony_ci } 32938c2ecf20Sopenharmony_ci if (msg->msg.data_len < 4) { 32948c2ecf20Sopenharmony_ci /* Message not big enough, just go on. */ 32958c2ecf20Sopenharmony_ci goto next_channel; 32968c2ecf20Sopenharmony_ci } 32978c2ecf20Sopenharmony_ci ch = intf->curr_channel; 32988c2ecf20Sopenharmony_ci chans = intf->wchannels[set].c; 32998c2ecf20Sopenharmony_ci chans[ch].medium = msg->msg.data[2] & 0x7f; 33008c2ecf20Sopenharmony_ci chans[ch].protocol = msg->msg.data[3] & 0x1f; 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_ci next_channel: 33038c2ecf20Sopenharmony_ci intf->curr_channel++; 33048c2ecf20Sopenharmony_ci if (intf->curr_channel >= IPMI_MAX_CHANNELS) { 33058c2ecf20Sopenharmony_ci intf->channel_list = intf->wchannels + set; 33068c2ecf20Sopenharmony_ci intf->channels_ready = true; 33078c2ecf20Sopenharmony_ci wake_up(&intf->waitq); 33088c2ecf20Sopenharmony_ci } else { 33098c2ecf20Sopenharmony_ci intf->channel_list = intf->wchannels + set; 33108c2ecf20Sopenharmony_ci intf->channels_ready = true; 33118c2ecf20Sopenharmony_ci rv = send_channel_info_cmd(intf, intf->curr_channel); 33128c2ecf20Sopenharmony_ci } 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci if (rv) { 33158c2ecf20Sopenharmony_ci /* Got an error somehow, just give up. */ 33168c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 33178c2ecf20Sopenharmony_ci "Error sending channel information for channel %d: %d\n", 33188c2ecf20Sopenharmony_ci intf->curr_channel, rv); 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci intf->channel_list = intf->wchannels + set; 33218c2ecf20Sopenharmony_ci intf->channels_ready = true; 33228c2ecf20Sopenharmony_ci wake_up(&intf->waitq); 33238c2ecf20Sopenharmony_ci } 33248c2ecf20Sopenharmony_ci } 33258c2ecf20Sopenharmony_ci out: 33268c2ecf20Sopenharmony_ci return; 33278c2ecf20Sopenharmony_ci} 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_ci/* 33308c2ecf20Sopenharmony_ci * Must be holding intf->bmc_reg_mutex to call this. 33318c2ecf20Sopenharmony_ci */ 33328c2ecf20Sopenharmony_cistatic int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id) 33338c2ecf20Sopenharmony_ci{ 33348c2ecf20Sopenharmony_ci int rv; 33358c2ecf20Sopenharmony_ci 33368c2ecf20Sopenharmony_ci if (ipmi_version_major(id) > 1 33378c2ecf20Sopenharmony_ci || (ipmi_version_major(id) == 1 33388c2ecf20Sopenharmony_ci && ipmi_version_minor(id) >= 5)) { 33398c2ecf20Sopenharmony_ci unsigned int set; 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci /* 33428c2ecf20Sopenharmony_ci * Start scanning the channels to see what is 33438c2ecf20Sopenharmony_ci * available. 33448c2ecf20Sopenharmony_ci */ 33458c2ecf20Sopenharmony_ci set = !intf->curr_working_cset; 33468c2ecf20Sopenharmony_ci intf->curr_working_cset = set; 33478c2ecf20Sopenharmony_ci memset(&intf->wchannels[set], 0, 33488c2ecf20Sopenharmony_ci sizeof(struct ipmi_channel_set)); 33498c2ecf20Sopenharmony_ci 33508c2ecf20Sopenharmony_ci intf->null_user_handler = channel_handler; 33518c2ecf20Sopenharmony_ci intf->curr_channel = 0; 33528c2ecf20Sopenharmony_ci rv = send_channel_info_cmd(intf, 0); 33538c2ecf20Sopenharmony_ci if (rv) { 33548c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 33558c2ecf20Sopenharmony_ci "Error sending channel information for channel 0, %d\n", 33568c2ecf20Sopenharmony_ci rv); 33578c2ecf20Sopenharmony_ci intf->null_user_handler = NULL; 33588c2ecf20Sopenharmony_ci return -EIO; 33598c2ecf20Sopenharmony_ci } 33608c2ecf20Sopenharmony_ci 33618c2ecf20Sopenharmony_ci /* Wait for the channel info to be read. */ 33628c2ecf20Sopenharmony_ci wait_event(intf->waitq, intf->channels_ready); 33638c2ecf20Sopenharmony_ci intf->null_user_handler = NULL; 33648c2ecf20Sopenharmony_ci } else { 33658c2ecf20Sopenharmony_ci unsigned int set = intf->curr_working_cset; 33668c2ecf20Sopenharmony_ci 33678c2ecf20Sopenharmony_ci /* Assume a single IPMB channel at zero. */ 33688c2ecf20Sopenharmony_ci intf->wchannels[set].c[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; 33698c2ecf20Sopenharmony_ci intf->wchannels[set].c[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; 33708c2ecf20Sopenharmony_ci intf->channel_list = intf->wchannels + set; 33718c2ecf20Sopenharmony_ci intf->channels_ready = true; 33728c2ecf20Sopenharmony_ci } 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_ci return 0; 33758c2ecf20Sopenharmony_ci} 33768c2ecf20Sopenharmony_ci 33778c2ecf20Sopenharmony_cistatic void ipmi_poll(struct ipmi_smi *intf) 33788c2ecf20Sopenharmony_ci{ 33798c2ecf20Sopenharmony_ci if (intf->handlers->poll) 33808c2ecf20Sopenharmony_ci intf->handlers->poll(intf->send_info); 33818c2ecf20Sopenharmony_ci /* In case something came in */ 33828c2ecf20Sopenharmony_ci handle_new_recv_msgs(intf); 33838c2ecf20Sopenharmony_ci} 33848c2ecf20Sopenharmony_ci 33858c2ecf20Sopenharmony_civoid ipmi_poll_interface(struct ipmi_user *user) 33868c2ecf20Sopenharmony_ci{ 33878c2ecf20Sopenharmony_ci ipmi_poll(user->intf); 33888c2ecf20Sopenharmony_ci} 33898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_poll_interface); 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_cistatic void redo_bmc_reg(struct work_struct *work) 33928c2ecf20Sopenharmony_ci{ 33938c2ecf20Sopenharmony_ci struct ipmi_smi *intf = container_of(work, struct ipmi_smi, 33948c2ecf20Sopenharmony_ci bmc_reg_work); 33958c2ecf20Sopenharmony_ci 33968c2ecf20Sopenharmony_ci if (!intf->in_shutdown) 33978c2ecf20Sopenharmony_ci bmc_get_device_id(intf, NULL, NULL, NULL, NULL); 33988c2ecf20Sopenharmony_ci 33998c2ecf20Sopenharmony_ci kref_put(&intf->refcount, intf_free); 34008c2ecf20Sopenharmony_ci} 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_ciint ipmi_add_smi(struct module *owner, 34038c2ecf20Sopenharmony_ci const struct ipmi_smi_handlers *handlers, 34048c2ecf20Sopenharmony_ci void *send_info, 34058c2ecf20Sopenharmony_ci struct device *si_dev, 34068c2ecf20Sopenharmony_ci unsigned char slave_addr) 34078c2ecf20Sopenharmony_ci{ 34088c2ecf20Sopenharmony_ci int i, j; 34098c2ecf20Sopenharmony_ci int rv; 34108c2ecf20Sopenharmony_ci struct ipmi_smi *intf, *tintf; 34118c2ecf20Sopenharmony_ci struct list_head *link; 34128c2ecf20Sopenharmony_ci struct ipmi_device_id id; 34138c2ecf20Sopenharmony_ci 34148c2ecf20Sopenharmony_ci /* 34158c2ecf20Sopenharmony_ci * Make sure the driver is actually initialized, this handles 34168c2ecf20Sopenharmony_ci * problems with initialization order. 34178c2ecf20Sopenharmony_ci */ 34188c2ecf20Sopenharmony_ci rv = ipmi_init_msghandler(); 34198c2ecf20Sopenharmony_ci if (rv) 34208c2ecf20Sopenharmony_ci return rv; 34218c2ecf20Sopenharmony_ci 34228c2ecf20Sopenharmony_ci intf = kzalloc(sizeof(*intf), GFP_KERNEL); 34238c2ecf20Sopenharmony_ci if (!intf) 34248c2ecf20Sopenharmony_ci return -ENOMEM; 34258c2ecf20Sopenharmony_ci 34268c2ecf20Sopenharmony_ci rv = init_srcu_struct(&intf->users_srcu); 34278c2ecf20Sopenharmony_ci if (rv) { 34288c2ecf20Sopenharmony_ci kfree(intf); 34298c2ecf20Sopenharmony_ci return rv; 34308c2ecf20Sopenharmony_ci } 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci intf->owner = owner; 34338c2ecf20Sopenharmony_ci intf->bmc = &intf->tmp_bmc; 34348c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->bmc->intfs); 34358c2ecf20Sopenharmony_ci mutex_init(&intf->bmc->dyn_mutex); 34368c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->bmc_link); 34378c2ecf20Sopenharmony_ci mutex_init(&intf->bmc_reg_mutex); 34388c2ecf20Sopenharmony_ci intf->intf_num = -1; /* Mark it invalid for now. */ 34398c2ecf20Sopenharmony_ci kref_init(&intf->refcount); 34408c2ecf20Sopenharmony_ci INIT_WORK(&intf->bmc_reg_work, redo_bmc_reg); 34418c2ecf20Sopenharmony_ci intf->si_dev = si_dev; 34428c2ecf20Sopenharmony_ci for (j = 0; j < IPMI_MAX_CHANNELS; j++) { 34438c2ecf20Sopenharmony_ci intf->addrinfo[j].address = IPMI_BMC_SLAVE_ADDR; 34448c2ecf20Sopenharmony_ci intf->addrinfo[j].lun = 2; 34458c2ecf20Sopenharmony_ci } 34468c2ecf20Sopenharmony_ci if (slave_addr != 0) 34478c2ecf20Sopenharmony_ci intf->addrinfo[0].address = slave_addr; 34488c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->users); 34498c2ecf20Sopenharmony_ci intf->handlers = handlers; 34508c2ecf20Sopenharmony_ci intf->send_info = send_info; 34518c2ecf20Sopenharmony_ci spin_lock_init(&intf->seq_lock); 34528c2ecf20Sopenharmony_ci for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) { 34538c2ecf20Sopenharmony_ci intf->seq_table[j].inuse = 0; 34548c2ecf20Sopenharmony_ci intf->seq_table[j].seqid = 0; 34558c2ecf20Sopenharmony_ci } 34568c2ecf20Sopenharmony_ci intf->curr_seq = 0; 34578c2ecf20Sopenharmony_ci spin_lock_init(&intf->waiting_rcv_msgs_lock); 34588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->waiting_rcv_msgs); 34598c2ecf20Sopenharmony_ci tasklet_setup(&intf->recv_tasklet, 34608c2ecf20Sopenharmony_ci smi_recv_tasklet); 34618c2ecf20Sopenharmony_ci atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0); 34628c2ecf20Sopenharmony_ci spin_lock_init(&intf->xmit_msgs_lock); 34638c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->xmit_msgs); 34648c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->hp_xmit_msgs); 34658c2ecf20Sopenharmony_ci spin_lock_init(&intf->events_lock); 34668c2ecf20Sopenharmony_ci spin_lock_init(&intf->watch_lock); 34678c2ecf20Sopenharmony_ci atomic_set(&intf->event_waiters, 0); 34688c2ecf20Sopenharmony_ci intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; 34698c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->waiting_events); 34708c2ecf20Sopenharmony_ci intf->waiting_events_count = 0; 34718c2ecf20Sopenharmony_ci mutex_init(&intf->cmd_rcvrs_mutex); 34728c2ecf20Sopenharmony_ci spin_lock_init(&intf->maintenance_mode_lock); 34738c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->cmd_rcvrs); 34748c2ecf20Sopenharmony_ci init_waitqueue_head(&intf->waitq); 34758c2ecf20Sopenharmony_ci for (i = 0; i < IPMI_NUM_STATS; i++) 34768c2ecf20Sopenharmony_ci atomic_set(&intf->stats[i], 0); 34778c2ecf20Sopenharmony_ci 34788c2ecf20Sopenharmony_ci mutex_lock(&ipmi_interfaces_mutex); 34798c2ecf20Sopenharmony_ci /* Look for a hole in the numbers. */ 34808c2ecf20Sopenharmony_ci i = 0; 34818c2ecf20Sopenharmony_ci link = &ipmi_interfaces; 34828c2ecf20Sopenharmony_ci list_for_each_entry_rcu(tintf, &ipmi_interfaces, link, 34838c2ecf20Sopenharmony_ci ipmi_interfaces_mutex_held()) { 34848c2ecf20Sopenharmony_ci if (tintf->intf_num != i) { 34858c2ecf20Sopenharmony_ci link = &tintf->link; 34868c2ecf20Sopenharmony_ci break; 34878c2ecf20Sopenharmony_ci } 34888c2ecf20Sopenharmony_ci i++; 34898c2ecf20Sopenharmony_ci } 34908c2ecf20Sopenharmony_ci /* Add the new interface in numeric order. */ 34918c2ecf20Sopenharmony_ci if (i == 0) 34928c2ecf20Sopenharmony_ci list_add_rcu(&intf->link, &ipmi_interfaces); 34938c2ecf20Sopenharmony_ci else 34948c2ecf20Sopenharmony_ci list_add_tail_rcu(&intf->link, link); 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci rv = handlers->start_processing(send_info, intf); 34978c2ecf20Sopenharmony_ci if (rv) 34988c2ecf20Sopenharmony_ci goto out_err; 34998c2ecf20Sopenharmony_ci 35008c2ecf20Sopenharmony_ci rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i); 35018c2ecf20Sopenharmony_ci if (rv) { 35028c2ecf20Sopenharmony_ci dev_err(si_dev, "Unable to get the device id: %d\n", rv); 35038c2ecf20Sopenharmony_ci goto out_err_started; 35048c2ecf20Sopenharmony_ci } 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_ci mutex_lock(&intf->bmc_reg_mutex); 35078c2ecf20Sopenharmony_ci rv = __scan_channels(intf, &id); 35088c2ecf20Sopenharmony_ci mutex_unlock(&intf->bmc_reg_mutex); 35098c2ecf20Sopenharmony_ci if (rv) 35108c2ecf20Sopenharmony_ci goto out_err_bmc_reg; 35118c2ecf20Sopenharmony_ci 35128c2ecf20Sopenharmony_ci /* 35138c2ecf20Sopenharmony_ci * Keep memory order straight for RCU readers. Make 35148c2ecf20Sopenharmony_ci * sure everything else is committed to memory before 35158c2ecf20Sopenharmony_ci * setting intf_num to mark the interface valid. 35168c2ecf20Sopenharmony_ci */ 35178c2ecf20Sopenharmony_ci smp_wmb(); 35188c2ecf20Sopenharmony_ci intf->intf_num = i; 35198c2ecf20Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci /* After this point the interface is legal to use. */ 35228c2ecf20Sopenharmony_ci call_smi_watchers(i, intf->si_dev); 35238c2ecf20Sopenharmony_ci 35248c2ecf20Sopenharmony_ci return 0; 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci out_err_bmc_reg: 35278c2ecf20Sopenharmony_ci ipmi_bmc_unregister(intf); 35288c2ecf20Sopenharmony_ci out_err_started: 35298c2ecf20Sopenharmony_ci if (intf->handlers->shutdown) 35308c2ecf20Sopenharmony_ci intf->handlers->shutdown(intf->send_info); 35318c2ecf20Sopenharmony_ci out_err: 35328c2ecf20Sopenharmony_ci list_del_rcu(&intf->link); 35338c2ecf20Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 35348c2ecf20Sopenharmony_ci synchronize_srcu(&ipmi_interfaces_srcu); 35358c2ecf20Sopenharmony_ci cleanup_srcu_struct(&intf->users_srcu); 35368c2ecf20Sopenharmony_ci kref_put(&intf->refcount, intf_free); 35378c2ecf20Sopenharmony_ci 35388c2ecf20Sopenharmony_ci return rv; 35398c2ecf20Sopenharmony_ci} 35408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_add_smi); 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_cistatic void deliver_smi_err_response(struct ipmi_smi *intf, 35438c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg, 35448c2ecf20Sopenharmony_ci unsigned char err) 35458c2ecf20Sopenharmony_ci{ 35468c2ecf20Sopenharmony_ci int rv; 35478c2ecf20Sopenharmony_ci msg->rsp[0] = msg->data[0] | 4; 35488c2ecf20Sopenharmony_ci msg->rsp[1] = msg->data[1]; 35498c2ecf20Sopenharmony_ci msg->rsp[2] = err; 35508c2ecf20Sopenharmony_ci msg->rsp_size = 3; 35518c2ecf20Sopenharmony_ci 35528c2ecf20Sopenharmony_ci /* This will never requeue, but it may ask us to free the message. */ 35538c2ecf20Sopenharmony_ci rv = handle_one_recv_msg(intf, msg); 35548c2ecf20Sopenharmony_ci if (rv == 0) 35558c2ecf20Sopenharmony_ci ipmi_free_smi_msg(msg); 35568c2ecf20Sopenharmony_ci} 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_cistatic void cleanup_smi_msgs(struct ipmi_smi *intf) 35598c2ecf20Sopenharmony_ci{ 35608c2ecf20Sopenharmony_ci int i; 35618c2ecf20Sopenharmony_ci struct seq_table *ent; 35628c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg; 35638c2ecf20Sopenharmony_ci struct list_head *entry; 35648c2ecf20Sopenharmony_ci struct list_head tmplist; 35658c2ecf20Sopenharmony_ci 35668c2ecf20Sopenharmony_ci /* Clear out our transmit queues and hold the messages. */ 35678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tmplist); 35688c2ecf20Sopenharmony_ci list_splice_tail(&intf->hp_xmit_msgs, &tmplist); 35698c2ecf20Sopenharmony_ci list_splice_tail(&intf->xmit_msgs, &tmplist); 35708c2ecf20Sopenharmony_ci 35718c2ecf20Sopenharmony_ci /* Current message first, to preserve order */ 35728c2ecf20Sopenharmony_ci while (intf->curr_msg && !list_empty(&intf->waiting_rcv_msgs)) { 35738c2ecf20Sopenharmony_ci /* Wait for the message to clear out. */ 35748c2ecf20Sopenharmony_ci schedule_timeout(1); 35758c2ecf20Sopenharmony_ci } 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_ci /* No need for locks, the interface is down. */ 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci /* 35808c2ecf20Sopenharmony_ci * Return errors for all pending messages in queue and in the 35818c2ecf20Sopenharmony_ci * tables waiting for remote responses. 35828c2ecf20Sopenharmony_ci */ 35838c2ecf20Sopenharmony_ci while (!list_empty(&tmplist)) { 35848c2ecf20Sopenharmony_ci entry = tmplist.next; 35858c2ecf20Sopenharmony_ci list_del(entry); 35868c2ecf20Sopenharmony_ci msg = list_entry(entry, struct ipmi_smi_msg, link); 35878c2ecf20Sopenharmony_ci deliver_smi_err_response(intf, msg, IPMI_ERR_UNSPECIFIED); 35888c2ecf20Sopenharmony_ci } 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { 35918c2ecf20Sopenharmony_ci ent = &intf->seq_table[i]; 35928c2ecf20Sopenharmony_ci if (!ent->inuse) 35938c2ecf20Sopenharmony_ci continue; 35948c2ecf20Sopenharmony_ci deliver_err_response(intf, ent->recv_msg, IPMI_ERR_UNSPECIFIED); 35958c2ecf20Sopenharmony_ci } 35968c2ecf20Sopenharmony_ci} 35978c2ecf20Sopenharmony_ci 35988c2ecf20Sopenharmony_civoid ipmi_unregister_smi(struct ipmi_smi *intf) 35998c2ecf20Sopenharmony_ci{ 36008c2ecf20Sopenharmony_ci struct ipmi_smi_watcher *w; 36018c2ecf20Sopenharmony_ci int intf_num = intf->intf_num, index; 36028c2ecf20Sopenharmony_ci 36038c2ecf20Sopenharmony_ci mutex_lock(&ipmi_interfaces_mutex); 36048c2ecf20Sopenharmony_ci intf->intf_num = -1; 36058c2ecf20Sopenharmony_ci intf->in_shutdown = true; 36068c2ecf20Sopenharmony_ci list_del_rcu(&intf->link); 36078c2ecf20Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 36088c2ecf20Sopenharmony_ci synchronize_srcu(&ipmi_interfaces_srcu); 36098c2ecf20Sopenharmony_ci 36108c2ecf20Sopenharmony_ci /* At this point no users can be added to the interface. */ 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci /* 36138c2ecf20Sopenharmony_ci * Call all the watcher interfaces to tell them that 36148c2ecf20Sopenharmony_ci * an interface is going away. 36158c2ecf20Sopenharmony_ci */ 36168c2ecf20Sopenharmony_ci mutex_lock(&smi_watchers_mutex); 36178c2ecf20Sopenharmony_ci list_for_each_entry(w, &smi_watchers, link) 36188c2ecf20Sopenharmony_ci w->smi_gone(intf_num); 36198c2ecf20Sopenharmony_ci mutex_unlock(&smi_watchers_mutex); 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci index = srcu_read_lock(&intf->users_srcu); 36228c2ecf20Sopenharmony_ci while (!list_empty(&intf->users)) { 36238c2ecf20Sopenharmony_ci struct ipmi_user *user = 36248c2ecf20Sopenharmony_ci container_of(list_next_rcu(&intf->users), 36258c2ecf20Sopenharmony_ci struct ipmi_user, link); 36268c2ecf20Sopenharmony_ci 36278c2ecf20Sopenharmony_ci _ipmi_destroy_user(user); 36288c2ecf20Sopenharmony_ci } 36298c2ecf20Sopenharmony_ci srcu_read_unlock(&intf->users_srcu, index); 36308c2ecf20Sopenharmony_ci 36318c2ecf20Sopenharmony_ci if (intf->handlers->shutdown) 36328c2ecf20Sopenharmony_ci intf->handlers->shutdown(intf->send_info); 36338c2ecf20Sopenharmony_ci 36348c2ecf20Sopenharmony_ci cleanup_smi_msgs(intf); 36358c2ecf20Sopenharmony_ci 36368c2ecf20Sopenharmony_ci ipmi_bmc_unregister(intf); 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci cleanup_srcu_struct(&intf->users_srcu); 36398c2ecf20Sopenharmony_ci kref_put(&intf->refcount, intf_free); 36408c2ecf20Sopenharmony_ci} 36418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_unregister_smi); 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_cistatic int handle_ipmb_get_msg_rsp(struct ipmi_smi *intf, 36448c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 36458c2ecf20Sopenharmony_ci{ 36468c2ecf20Sopenharmony_ci struct ipmi_ipmb_addr ipmb_addr; 36478c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 36488c2ecf20Sopenharmony_ci 36498c2ecf20Sopenharmony_ci /* 36508c2ecf20Sopenharmony_ci * This is 11, not 10, because the response must contain a 36518c2ecf20Sopenharmony_ci * completion code. 36528c2ecf20Sopenharmony_ci */ 36538c2ecf20Sopenharmony_ci if (msg->rsp_size < 11) { 36548c2ecf20Sopenharmony_ci /* Message not big enough, just ignore it. */ 36558c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, invalid_ipmb_responses); 36568c2ecf20Sopenharmony_ci return 0; 36578c2ecf20Sopenharmony_ci } 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci if (msg->rsp[2] != 0) { 36608c2ecf20Sopenharmony_ci /* An error getting the response, just ignore it. */ 36618c2ecf20Sopenharmony_ci return 0; 36628c2ecf20Sopenharmony_ci } 36638c2ecf20Sopenharmony_ci 36648c2ecf20Sopenharmony_ci ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; 36658c2ecf20Sopenharmony_ci ipmb_addr.slave_addr = msg->rsp[6]; 36668c2ecf20Sopenharmony_ci ipmb_addr.channel = msg->rsp[3] & 0x0f; 36678c2ecf20Sopenharmony_ci ipmb_addr.lun = msg->rsp[7] & 3; 36688c2ecf20Sopenharmony_ci 36698c2ecf20Sopenharmony_ci /* 36708c2ecf20Sopenharmony_ci * It's a response from a remote entity. Look up the sequence 36718c2ecf20Sopenharmony_ci * number and handle the response. 36728c2ecf20Sopenharmony_ci */ 36738c2ecf20Sopenharmony_ci if (intf_find_seq(intf, 36748c2ecf20Sopenharmony_ci msg->rsp[7] >> 2, 36758c2ecf20Sopenharmony_ci msg->rsp[3] & 0x0f, 36768c2ecf20Sopenharmony_ci msg->rsp[8], 36778c2ecf20Sopenharmony_ci (msg->rsp[4] >> 2) & (~1), 36788c2ecf20Sopenharmony_ci (struct ipmi_addr *) &ipmb_addr, 36798c2ecf20Sopenharmony_ci &recv_msg)) { 36808c2ecf20Sopenharmony_ci /* 36818c2ecf20Sopenharmony_ci * We were unable to find the sequence number, 36828c2ecf20Sopenharmony_ci * so just nuke the message. 36838c2ecf20Sopenharmony_ci */ 36848c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_ipmb_responses); 36858c2ecf20Sopenharmony_ci return 0; 36868c2ecf20Sopenharmony_ci } 36878c2ecf20Sopenharmony_ci 36888c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[9], msg->rsp_size - 9); 36898c2ecf20Sopenharmony_ci /* 36908c2ecf20Sopenharmony_ci * The other fields matched, so no need to set them, except 36918c2ecf20Sopenharmony_ci * for netfn, which needs to be the response that was 36928c2ecf20Sopenharmony_ci * returned, not the request value. 36938c2ecf20Sopenharmony_ci */ 36948c2ecf20Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[4] >> 2; 36958c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 36968c2ecf20Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 10; 36978c2ecf20Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 36988c2ecf20Sopenharmony_ci if (deliver_response(intf, recv_msg)) 36998c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_ipmb_responses); 37008c2ecf20Sopenharmony_ci else 37018c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, handled_ipmb_responses); 37028c2ecf20Sopenharmony_ci 37038c2ecf20Sopenharmony_ci return 0; 37048c2ecf20Sopenharmony_ci} 37058c2ecf20Sopenharmony_ci 37068c2ecf20Sopenharmony_cistatic int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, 37078c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 37088c2ecf20Sopenharmony_ci{ 37098c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr; 37108c2ecf20Sopenharmony_ci int rv = 0; 37118c2ecf20Sopenharmony_ci unsigned char netfn; 37128c2ecf20Sopenharmony_ci unsigned char cmd; 37138c2ecf20Sopenharmony_ci unsigned char chan; 37148c2ecf20Sopenharmony_ci struct ipmi_user *user = NULL; 37158c2ecf20Sopenharmony_ci struct ipmi_ipmb_addr *ipmb_addr; 37168c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 37178c2ecf20Sopenharmony_ci 37188c2ecf20Sopenharmony_ci if (msg->rsp_size < 10) { 37198c2ecf20Sopenharmony_ci /* Message not big enough, just ignore it. */ 37208c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, invalid_commands); 37218c2ecf20Sopenharmony_ci return 0; 37228c2ecf20Sopenharmony_ci } 37238c2ecf20Sopenharmony_ci 37248c2ecf20Sopenharmony_ci if (msg->rsp[2] != 0) { 37258c2ecf20Sopenharmony_ci /* An error getting the response, just ignore it. */ 37268c2ecf20Sopenharmony_ci return 0; 37278c2ecf20Sopenharmony_ci } 37288c2ecf20Sopenharmony_ci 37298c2ecf20Sopenharmony_ci netfn = msg->rsp[4] >> 2; 37308c2ecf20Sopenharmony_ci cmd = msg->rsp[8]; 37318c2ecf20Sopenharmony_ci chan = msg->rsp[3] & 0xf; 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci rcu_read_lock(); 37348c2ecf20Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); 37358c2ecf20Sopenharmony_ci if (rcvr) { 37368c2ecf20Sopenharmony_ci user = rcvr->user; 37378c2ecf20Sopenharmony_ci kref_get(&user->refcount); 37388c2ecf20Sopenharmony_ci } else 37398c2ecf20Sopenharmony_ci user = NULL; 37408c2ecf20Sopenharmony_ci rcu_read_unlock(); 37418c2ecf20Sopenharmony_ci 37428c2ecf20Sopenharmony_ci if (user == NULL) { 37438c2ecf20Sopenharmony_ci /* We didn't find a user, deliver an error response. */ 37448c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 37478c2ecf20Sopenharmony_ci msg->data[1] = IPMI_SEND_MSG_CMD; 37488c2ecf20Sopenharmony_ci msg->data[2] = msg->rsp[3]; 37498c2ecf20Sopenharmony_ci msg->data[3] = msg->rsp[6]; 37508c2ecf20Sopenharmony_ci msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); 37518c2ecf20Sopenharmony_ci msg->data[5] = ipmb_checksum(&msg->data[3], 2); 37528c2ecf20Sopenharmony_ci msg->data[6] = intf->addrinfo[msg->rsp[3] & 0xf].address; 37538c2ecf20Sopenharmony_ci /* rqseq/lun */ 37548c2ecf20Sopenharmony_ci msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); 37558c2ecf20Sopenharmony_ci msg->data[8] = msg->rsp[8]; /* cmd */ 37568c2ecf20Sopenharmony_ci msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE; 37578c2ecf20Sopenharmony_ci msg->data[10] = ipmb_checksum(&msg->data[6], 4); 37588c2ecf20Sopenharmony_ci msg->data_size = 11; 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_ci pr_debug("Invalid command: %*ph\n", msg->data_size, msg->data); 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci rcu_read_lock(); 37638c2ecf20Sopenharmony_ci if (!intf->in_shutdown) { 37648c2ecf20Sopenharmony_ci smi_send(intf, intf->handlers, msg, 0); 37658c2ecf20Sopenharmony_ci /* 37668c2ecf20Sopenharmony_ci * We used the message, so return the value 37678c2ecf20Sopenharmony_ci * that causes it to not be freed or 37688c2ecf20Sopenharmony_ci * queued. 37698c2ecf20Sopenharmony_ci */ 37708c2ecf20Sopenharmony_ci rv = -1; 37718c2ecf20Sopenharmony_ci } 37728c2ecf20Sopenharmony_ci rcu_read_unlock(); 37738c2ecf20Sopenharmony_ci } else { 37748c2ecf20Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 37758c2ecf20Sopenharmony_ci if (!recv_msg) { 37768c2ecf20Sopenharmony_ci /* 37778c2ecf20Sopenharmony_ci * We couldn't allocate memory for the 37788c2ecf20Sopenharmony_ci * message, so requeue it for handling 37798c2ecf20Sopenharmony_ci * later. 37808c2ecf20Sopenharmony_ci */ 37818c2ecf20Sopenharmony_ci rv = 1; 37828c2ecf20Sopenharmony_ci kref_put(&user->refcount, free_user); 37838c2ecf20Sopenharmony_ci } else { 37848c2ecf20Sopenharmony_ci /* Extract the source address from the data. */ 37858c2ecf20Sopenharmony_ci ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr; 37868c2ecf20Sopenharmony_ci ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE; 37878c2ecf20Sopenharmony_ci ipmb_addr->slave_addr = msg->rsp[6]; 37888c2ecf20Sopenharmony_ci ipmb_addr->lun = msg->rsp[7] & 3; 37898c2ecf20Sopenharmony_ci ipmb_addr->channel = msg->rsp[3] & 0xf; 37908c2ecf20Sopenharmony_ci 37918c2ecf20Sopenharmony_ci /* 37928c2ecf20Sopenharmony_ci * Extract the rest of the message information 37938c2ecf20Sopenharmony_ci * from the IPMB header. 37948c2ecf20Sopenharmony_ci */ 37958c2ecf20Sopenharmony_ci recv_msg->user = user; 37968c2ecf20Sopenharmony_ci recv_msg->recv_type = IPMI_CMD_RECV_TYPE; 37978c2ecf20Sopenharmony_ci recv_msg->msgid = msg->rsp[7] >> 2; 37988c2ecf20Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[4] >> 2; 37998c2ecf20Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[8]; 38008c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 38018c2ecf20Sopenharmony_ci 38028c2ecf20Sopenharmony_ci /* 38038c2ecf20Sopenharmony_ci * We chop off 10, not 9 bytes because the checksum 38048c2ecf20Sopenharmony_ci * at the end also needs to be removed. 38058c2ecf20Sopenharmony_ci */ 38068c2ecf20Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 10; 38078c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[9], 38088c2ecf20Sopenharmony_ci msg->rsp_size - 10); 38098c2ecf20Sopenharmony_ci if (deliver_response(intf, recv_msg)) 38108c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 38118c2ecf20Sopenharmony_ci else 38128c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, handled_commands); 38138c2ecf20Sopenharmony_ci } 38148c2ecf20Sopenharmony_ci } 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_ci return rv; 38178c2ecf20Sopenharmony_ci} 38188c2ecf20Sopenharmony_ci 38198c2ecf20Sopenharmony_cistatic int handle_lan_get_msg_rsp(struct ipmi_smi *intf, 38208c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 38218c2ecf20Sopenharmony_ci{ 38228c2ecf20Sopenharmony_ci struct ipmi_lan_addr lan_addr; 38238c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_ci 38268c2ecf20Sopenharmony_ci /* 38278c2ecf20Sopenharmony_ci * This is 13, not 12, because the response must contain a 38288c2ecf20Sopenharmony_ci * completion code. 38298c2ecf20Sopenharmony_ci */ 38308c2ecf20Sopenharmony_ci if (msg->rsp_size < 13) { 38318c2ecf20Sopenharmony_ci /* Message not big enough, just ignore it. */ 38328c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, invalid_lan_responses); 38338c2ecf20Sopenharmony_ci return 0; 38348c2ecf20Sopenharmony_ci } 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ci if (msg->rsp[2] != 0) { 38378c2ecf20Sopenharmony_ci /* An error getting the response, just ignore it. */ 38388c2ecf20Sopenharmony_ci return 0; 38398c2ecf20Sopenharmony_ci } 38408c2ecf20Sopenharmony_ci 38418c2ecf20Sopenharmony_ci lan_addr.addr_type = IPMI_LAN_ADDR_TYPE; 38428c2ecf20Sopenharmony_ci lan_addr.session_handle = msg->rsp[4]; 38438c2ecf20Sopenharmony_ci lan_addr.remote_SWID = msg->rsp[8]; 38448c2ecf20Sopenharmony_ci lan_addr.local_SWID = msg->rsp[5]; 38458c2ecf20Sopenharmony_ci lan_addr.channel = msg->rsp[3] & 0x0f; 38468c2ecf20Sopenharmony_ci lan_addr.privilege = msg->rsp[3] >> 4; 38478c2ecf20Sopenharmony_ci lan_addr.lun = msg->rsp[9] & 3; 38488c2ecf20Sopenharmony_ci 38498c2ecf20Sopenharmony_ci /* 38508c2ecf20Sopenharmony_ci * It's a response from a remote entity. Look up the sequence 38518c2ecf20Sopenharmony_ci * number and handle the response. 38528c2ecf20Sopenharmony_ci */ 38538c2ecf20Sopenharmony_ci if (intf_find_seq(intf, 38548c2ecf20Sopenharmony_ci msg->rsp[9] >> 2, 38558c2ecf20Sopenharmony_ci msg->rsp[3] & 0x0f, 38568c2ecf20Sopenharmony_ci msg->rsp[10], 38578c2ecf20Sopenharmony_ci (msg->rsp[6] >> 2) & (~1), 38588c2ecf20Sopenharmony_ci (struct ipmi_addr *) &lan_addr, 38598c2ecf20Sopenharmony_ci &recv_msg)) { 38608c2ecf20Sopenharmony_ci /* 38618c2ecf20Sopenharmony_ci * We were unable to find the sequence number, 38628c2ecf20Sopenharmony_ci * so just nuke the message. 38638c2ecf20Sopenharmony_ci */ 38648c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_lan_responses); 38658c2ecf20Sopenharmony_ci return 0; 38668c2ecf20Sopenharmony_ci } 38678c2ecf20Sopenharmony_ci 38688c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[11], msg->rsp_size - 11); 38698c2ecf20Sopenharmony_ci /* 38708c2ecf20Sopenharmony_ci * The other fields matched, so no need to set them, except 38718c2ecf20Sopenharmony_ci * for netfn, which needs to be the response that was 38728c2ecf20Sopenharmony_ci * returned, not the request value. 38738c2ecf20Sopenharmony_ci */ 38748c2ecf20Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[6] >> 2; 38758c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 38768c2ecf20Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 12; 38778c2ecf20Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 38788c2ecf20Sopenharmony_ci if (deliver_response(intf, recv_msg)) 38798c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_lan_responses); 38808c2ecf20Sopenharmony_ci else 38818c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, handled_lan_responses); 38828c2ecf20Sopenharmony_ci 38838c2ecf20Sopenharmony_ci return 0; 38848c2ecf20Sopenharmony_ci} 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_cistatic int handle_lan_get_msg_cmd(struct ipmi_smi *intf, 38878c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 38888c2ecf20Sopenharmony_ci{ 38898c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr; 38908c2ecf20Sopenharmony_ci int rv = 0; 38918c2ecf20Sopenharmony_ci unsigned char netfn; 38928c2ecf20Sopenharmony_ci unsigned char cmd; 38938c2ecf20Sopenharmony_ci unsigned char chan; 38948c2ecf20Sopenharmony_ci struct ipmi_user *user = NULL; 38958c2ecf20Sopenharmony_ci struct ipmi_lan_addr *lan_addr; 38968c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 38978c2ecf20Sopenharmony_ci 38988c2ecf20Sopenharmony_ci if (msg->rsp_size < 12) { 38998c2ecf20Sopenharmony_ci /* Message not big enough, just ignore it. */ 39008c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, invalid_commands); 39018c2ecf20Sopenharmony_ci return 0; 39028c2ecf20Sopenharmony_ci } 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci if (msg->rsp[2] != 0) { 39058c2ecf20Sopenharmony_ci /* An error getting the response, just ignore it. */ 39068c2ecf20Sopenharmony_ci return 0; 39078c2ecf20Sopenharmony_ci } 39088c2ecf20Sopenharmony_ci 39098c2ecf20Sopenharmony_ci netfn = msg->rsp[6] >> 2; 39108c2ecf20Sopenharmony_ci cmd = msg->rsp[10]; 39118c2ecf20Sopenharmony_ci chan = msg->rsp[3] & 0xf; 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci rcu_read_lock(); 39148c2ecf20Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); 39158c2ecf20Sopenharmony_ci if (rcvr) { 39168c2ecf20Sopenharmony_ci user = rcvr->user; 39178c2ecf20Sopenharmony_ci kref_get(&user->refcount); 39188c2ecf20Sopenharmony_ci } else 39198c2ecf20Sopenharmony_ci user = NULL; 39208c2ecf20Sopenharmony_ci rcu_read_unlock(); 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci if (user == NULL) { 39238c2ecf20Sopenharmony_ci /* We didn't find a user, just give up. */ 39248c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_ci /* 39278c2ecf20Sopenharmony_ci * Don't do anything with these messages, just allow 39288c2ecf20Sopenharmony_ci * them to be freed. 39298c2ecf20Sopenharmony_ci */ 39308c2ecf20Sopenharmony_ci rv = 0; 39318c2ecf20Sopenharmony_ci } else { 39328c2ecf20Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 39338c2ecf20Sopenharmony_ci if (!recv_msg) { 39348c2ecf20Sopenharmony_ci /* 39358c2ecf20Sopenharmony_ci * We couldn't allocate memory for the 39368c2ecf20Sopenharmony_ci * message, so requeue it for handling later. 39378c2ecf20Sopenharmony_ci */ 39388c2ecf20Sopenharmony_ci rv = 1; 39398c2ecf20Sopenharmony_ci kref_put(&user->refcount, free_user); 39408c2ecf20Sopenharmony_ci } else { 39418c2ecf20Sopenharmony_ci /* Extract the source address from the data. */ 39428c2ecf20Sopenharmony_ci lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr; 39438c2ecf20Sopenharmony_ci lan_addr->addr_type = IPMI_LAN_ADDR_TYPE; 39448c2ecf20Sopenharmony_ci lan_addr->session_handle = msg->rsp[4]; 39458c2ecf20Sopenharmony_ci lan_addr->remote_SWID = msg->rsp[8]; 39468c2ecf20Sopenharmony_ci lan_addr->local_SWID = msg->rsp[5]; 39478c2ecf20Sopenharmony_ci lan_addr->lun = msg->rsp[9] & 3; 39488c2ecf20Sopenharmony_ci lan_addr->channel = msg->rsp[3] & 0xf; 39498c2ecf20Sopenharmony_ci lan_addr->privilege = msg->rsp[3] >> 4; 39508c2ecf20Sopenharmony_ci 39518c2ecf20Sopenharmony_ci /* 39528c2ecf20Sopenharmony_ci * Extract the rest of the message information 39538c2ecf20Sopenharmony_ci * from the IPMB header. 39548c2ecf20Sopenharmony_ci */ 39558c2ecf20Sopenharmony_ci recv_msg->user = user; 39568c2ecf20Sopenharmony_ci recv_msg->recv_type = IPMI_CMD_RECV_TYPE; 39578c2ecf20Sopenharmony_ci recv_msg->msgid = msg->rsp[9] >> 2; 39588c2ecf20Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[6] >> 2; 39598c2ecf20Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[10]; 39608c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 39618c2ecf20Sopenharmony_ci 39628c2ecf20Sopenharmony_ci /* 39638c2ecf20Sopenharmony_ci * We chop off 12, not 11 bytes because the checksum 39648c2ecf20Sopenharmony_ci * at the end also needs to be removed. 39658c2ecf20Sopenharmony_ci */ 39668c2ecf20Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 12; 39678c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[11], 39688c2ecf20Sopenharmony_ci msg->rsp_size - 12); 39698c2ecf20Sopenharmony_ci if (deliver_response(intf, recv_msg)) 39708c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 39718c2ecf20Sopenharmony_ci else 39728c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, handled_commands); 39738c2ecf20Sopenharmony_ci } 39748c2ecf20Sopenharmony_ci } 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ci return rv; 39778c2ecf20Sopenharmony_ci} 39788c2ecf20Sopenharmony_ci 39798c2ecf20Sopenharmony_ci/* 39808c2ecf20Sopenharmony_ci * This routine will handle "Get Message" command responses with 39818c2ecf20Sopenharmony_ci * channels that use an OEM Medium. The message format belongs to 39828c2ecf20Sopenharmony_ci * the OEM. See IPMI 2.0 specification, Chapter 6 and 39838c2ecf20Sopenharmony_ci * Chapter 22, sections 22.6 and 22.24 for more details. 39848c2ecf20Sopenharmony_ci */ 39858c2ecf20Sopenharmony_cistatic int handle_oem_get_msg_cmd(struct ipmi_smi *intf, 39868c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 39878c2ecf20Sopenharmony_ci{ 39888c2ecf20Sopenharmony_ci struct cmd_rcvr *rcvr; 39898c2ecf20Sopenharmony_ci int rv = 0; 39908c2ecf20Sopenharmony_ci unsigned char netfn; 39918c2ecf20Sopenharmony_ci unsigned char cmd; 39928c2ecf20Sopenharmony_ci unsigned char chan; 39938c2ecf20Sopenharmony_ci struct ipmi_user *user = NULL; 39948c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr; 39958c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 39968c2ecf20Sopenharmony_ci 39978c2ecf20Sopenharmony_ci /* 39988c2ecf20Sopenharmony_ci * We expect the OEM SW to perform error checking 39998c2ecf20Sopenharmony_ci * so we just do some basic sanity checks 40008c2ecf20Sopenharmony_ci */ 40018c2ecf20Sopenharmony_ci if (msg->rsp_size < 4) { 40028c2ecf20Sopenharmony_ci /* Message not big enough, just ignore it. */ 40038c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, invalid_commands); 40048c2ecf20Sopenharmony_ci return 0; 40058c2ecf20Sopenharmony_ci } 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_ci if (msg->rsp[2] != 0) { 40088c2ecf20Sopenharmony_ci /* An error getting the response, just ignore it. */ 40098c2ecf20Sopenharmony_ci return 0; 40108c2ecf20Sopenharmony_ci } 40118c2ecf20Sopenharmony_ci 40128c2ecf20Sopenharmony_ci /* 40138c2ecf20Sopenharmony_ci * This is an OEM Message so the OEM needs to know how 40148c2ecf20Sopenharmony_ci * handle the message. We do no interpretation. 40158c2ecf20Sopenharmony_ci */ 40168c2ecf20Sopenharmony_ci netfn = msg->rsp[0] >> 2; 40178c2ecf20Sopenharmony_ci cmd = msg->rsp[1]; 40188c2ecf20Sopenharmony_ci chan = msg->rsp[3] & 0xf; 40198c2ecf20Sopenharmony_ci 40208c2ecf20Sopenharmony_ci rcu_read_lock(); 40218c2ecf20Sopenharmony_ci rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); 40228c2ecf20Sopenharmony_ci if (rcvr) { 40238c2ecf20Sopenharmony_ci user = rcvr->user; 40248c2ecf20Sopenharmony_ci kref_get(&user->refcount); 40258c2ecf20Sopenharmony_ci } else 40268c2ecf20Sopenharmony_ci user = NULL; 40278c2ecf20Sopenharmony_ci rcu_read_unlock(); 40288c2ecf20Sopenharmony_ci 40298c2ecf20Sopenharmony_ci if (user == NULL) { 40308c2ecf20Sopenharmony_ci /* We didn't find a user, just give up. */ 40318c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 40328c2ecf20Sopenharmony_ci 40338c2ecf20Sopenharmony_ci /* 40348c2ecf20Sopenharmony_ci * Don't do anything with these messages, just allow 40358c2ecf20Sopenharmony_ci * them to be freed. 40368c2ecf20Sopenharmony_ci */ 40378c2ecf20Sopenharmony_ci 40388c2ecf20Sopenharmony_ci rv = 0; 40398c2ecf20Sopenharmony_ci } else { 40408c2ecf20Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 40418c2ecf20Sopenharmony_ci if (!recv_msg) { 40428c2ecf20Sopenharmony_ci /* 40438c2ecf20Sopenharmony_ci * We couldn't allocate memory for the 40448c2ecf20Sopenharmony_ci * message, so requeue it for handling 40458c2ecf20Sopenharmony_ci * later. 40468c2ecf20Sopenharmony_ci */ 40478c2ecf20Sopenharmony_ci rv = 1; 40488c2ecf20Sopenharmony_ci kref_put(&user->refcount, free_user); 40498c2ecf20Sopenharmony_ci } else { 40508c2ecf20Sopenharmony_ci /* 40518c2ecf20Sopenharmony_ci * OEM Messages are expected to be delivered via 40528c2ecf20Sopenharmony_ci * the system interface to SMS software. We might 40538c2ecf20Sopenharmony_ci * need to visit this again depending on OEM 40548c2ecf20Sopenharmony_ci * requirements 40558c2ecf20Sopenharmony_ci */ 40568c2ecf20Sopenharmony_ci smi_addr = ((struct ipmi_system_interface_addr *) 40578c2ecf20Sopenharmony_ci &recv_msg->addr); 40588c2ecf20Sopenharmony_ci smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 40598c2ecf20Sopenharmony_ci smi_addr->channel = IPMI_BMC_CHANNEL; 40608c2ecf20Sopenharmony_ci smi_addr->lun = msg->rsp[0] & 3; 40618c2ecf20Sopenharmony_ci 40628c2ecf20Sopenharmony_ci recv_msg->user = user; 40638c2ecf20Sopenharmony_ci recv_msg->user_msg_data = NULL; 40648c2ecf20Sopenharmony_ci recv_msg->recv_type = IPMI_OEM_RECV_TYPE; 40658c2ecf20Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[0] >> 2; 40668c2ecf20Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[1]; 40678c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 40688c2ecf20Sopenharmony_ci 40698c2ecf20Sopenharmony_ci /* 40708c2ecf20Sopenharmony_ci * The message starts at byte 4 which follows the 40718c2ecf20Sopenharmony_ci * the Channel Byte in the "GET MESSAGE" command 40728c2ecf20Sopenharmony_ci */ 40738c2ecf20Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 4; 40748c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[4], 40758c2ecf20Sopenharmony_ci msg->rsp_size - 4); 40768c2ecf20Sopenharmony_ci if (deliver_response(intf, recv_msg)) 40778c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, unhandled_commands); 40788c2ecf20Sopenharmony_ci else 40798c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, handled_commands); 40808c2ecf20Sopenharmony_ci } 40818c2ecf20Sopenharmony_ci } 40828c2ecf20Sopenharmony_ci 40838c2ecf20Sopenharmony_ci return rv; 40848c2ecf20Sopenharmony_ci} 40858c2ecf20Sopenharmony_ci 40868c2ecf20Sopenharmony_cistatic void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg, 40878c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 40888c2ecf20Sopenharmony_ci{ 40898c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr; 40908c2ecf20Sopenharmony_ci 40918c2ecf20Sopenharmony_ci recv_msg->msgid = 0; 40928c2ecf20Sopenharmony_ci smi_addr = (struct ipmi_system_interface_addr *) &recv_msg->addr; 40938c2ecf20Sopenharmony_ci smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 40948c2ecf20Sopenharmony_ci smi_addr->channel = IPMI_BMC_CHANNEL; 40958c2ecf20Sopenharmony_ci smi_addr->lun = msg->rsp[0] & 3; 40968c2ecf20Sopenharmony_ci recv_msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE; 40978c2ecf20Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[0] >> 2; 40988c2ecf20Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[1]; 40998c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[3], msg->rsp_size - 3); 41008c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 41018c2ecf20Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 3; 41028c2ecf20Sopenharmony_ci} 41038c2ecf20Sopenharmony_ci 41048c2ecf20Sopenharmony_cistatic int handle_read_event_rsp(struct ipmi_smi *intf, 41058c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 41068c2ecf20Sopenharmony_ci{ 41078c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg, *recv_msg2; 41088c2ecf20Sopenharmony_ci struct list_head msgs; 41098c2ecf20Sopenharmony_ci struct ipmi_user *user; 41108c2ecf20Sopenharmony_ci int rv = 0, deliver_count = 0, index; 41118c2ecf20Sopenharmony_ci unsigned long flags; 41128c2ecf20Sopenharmony_ci 41138c2ecf20Sopenharmony_ci if (msg->rsp_size < 19) { 41148c2ecf20Sopenharmony_ci /* Message is too small to be an IPMB event. */ 41158c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, invalid_events); 41168c2ecf20Sopenharmony_ci return 0; 41178c2ecf20Sopenharmony_ci } 41188c2ecf20Sopenharmony_ci 41198c2ecf20Sopenharmony_ci if (msg->rsp[2] != 0) { 41208c2ecf20Sopenharmony_ci /* An error getting the event, just ignore it. */ 41218c2ecf20Sopenharmony_ci return 0; 41228c2ecf20Sopenharmony_ci } 41238c2ecf20Sopenharmony_ci 41248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&msgs); 41258c2ecf20Sopenharmony_ci 41268c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->events_lock, flags); 41278c2ecf20Sopenharmony_ci 41288c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, events); 41298c2ecf20Sopenharmony_ci 41308c2ecf20Sopenharmony_ci /* 41318c2ecf20Sopenharmony_ci * Allocate and fill in one message for every user that is 41328c2ecf20Sopenharmony_ci * getting events. 41338c2ecf20Sopenharmony_ci */ 41348c2ecf20Sopenharmony_ci index = srcu_read_lock(&intf->users_srcu); 41358c2ecf20Sopenharmony_ci list_for_each_entry_rcu(user, &intf->users, link) { 41368c2ecf20Sopenharmony_ci if (!user->gets_events) 41378c2ecf20Sopenharmony_ci continue; 41388c2ecf20Sopenharmony_ci 41398c2ecf20Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 41408c2ecf20Sopenharmony_ci if (!recv_msg) { 41418c2ecf20Sopenharmony_ci rcu_read_unlock(); 41428c2ecf20Sopenharmony_ci list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, 41438c2ecf20Sopenharmony_ci link) { 41448c2ecf20Sopenharmony_ci list_del(&recv_msg->link); 41458c2ecf20Sopenharmony_ci ipmi_free_recv_msg(recv_msg); 41468c2ecf20Sopenharmony_ci } 41478c2ecf20Sopenharmony_ci /* 41488c2ecf20Sopenharmony_ci * We couldn't allocate memory for the 41498c2ecf20Sopenharmony_ci * message, so requeue it for handling 41508c2ecf20Sopenharmony_ci * later. 41518c2ecf20Sopenharmony_ci */ 41528c2ecf20Sopenharmony_ci rv = 1; 41538c2ecf20Sopenharmony_ci goto out; 41548c2ecf20Sopenharmony_ci } 41558c2ecf20Sopenharmony_ci 41568c2ecf20Sopenharmony_ci deliver_count++; 41578c2ecf20Sopenharmony_ci 41588c2ecf20Sopenharmony_ci copy_event_into_recv_msg(recv_msg, msg); 41598c2ecf20Sopenharmony_ci recv_msg->user = user; 41608c2ecf20Sopenharmony_ci kref_get(&user->refcount); 41618c2ecf20Sopenharmony_ci list_add_tail(&recv_msg->link, &msgs); 41628c2ecf20Sopenharmony_ci } 41638c2ecf20Sopenharmony_ci srcu_read_unlock(&intf->users_srcu, index); 41648c2ecf20Sopenharmony_ci 41658c2ecf20Sopenharmony_ci if (deliver_count) { 41668c2ecf20Sopenharmony_ci /* Now deliver all the messages. */ 41678c2ecf20Sopenharmony_ci list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { 41688c2ecf20Sopenharmony_ci list_del(&recv_msg->link); 41698c2ecf20Sopenharmony_ci deliver_local_response(intf, recv_msg); 41708c2ecf20Sopenharmony_ci } 41718c2ecf20Sopenharmony_ci } else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) { 41728c2ecf20Sopenharmony_ci /* 41738c2ecf20Sopenharmony_ci * No one to receive the message, put it in queue if there's 41748c2ecf20Sopenharmony_ci * not already too many things in the queue. 41758c2ecf20Sopenharmony_ci */ 41768c2ecf20Sopenharmony_ci recv_msg = ipmi_alloc_recv_msg(); 41778c2ecf20Sopenharmony_ci if (!recv_msg) { 41788c2ecf20Sopenharmony_ci /* 41798c2ecf20Sopenharmony_ci * We couldn't allocate memory for the 41808c2ecf20Sopenharmony_ci * message, so requeue it for handling 41818c2ecf20Sopenharmony_ci * later. 41828c2ecf20Sopenharmony_ci */ 41838c2ecf20Sopenharmony_ci rv = 1; 41848c2ecf20Sopenharmony_ci goto out; 41858c2ecf20Sopenharmony_ci } 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_ci copy_event_into_recv_msg(recv_msg, msg); 41888c2ecf20Sopenharmony_ci list_add_tail(&recv_msg->link, &intf->waiting_events); 41898c2ecf20Sopenharmony_ci intf->waiting_events_count++; 41908c2ecf20Sopenharmony_ci } else if (!intf->event_msg_printed) { 41918c2ecf20Sopenharmony_ci /* 41928c2ecf20Sopenharmony_ci * There's too many things in the queue, discard this 41938c2ecf20Sopenharmony_ci * message. 41948c2ecf20Sopenharmony_ci */ 41958c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 41968c2ecf20Sopenharmony_ci "Event queue full, discarding incoming events\n"); 41978c2ecf20Sopenharmony_ci intf->event_msg_printed = 1; 41988c2ecf20Sopenharmony_ci } 41998c2ecf20Sopenharmony_ci 42008c2ecf20Sopenharmony_ci out: 42018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->events_lock, flags); 42028c2ecf20Sopenharmony_ci 42038c2ecf20Sopenharmony_ci return rv; 42048c2ecf20Sopenharmony_ci} 42058c2ecf20Sopenharmony_ci 42068c2ecf20Sopenharmony_cistatic int handle_bmc_rsp(struct ipmi_smi *intf, 42078c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 42088c2ecf20Sopenharmony_ci{ 42098c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg; 42108c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr *smi_addr; 42118c2ecf20Sopenharmony_ci 42128c2ecf20Sopenharmony_ci recv_msg = (struct ipmi_recv_msg *) msg->user_data; 42138c2ecf20Sopenharmony_ci if (recv_msg == NULL) { 42148c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 42158c2ecf20Sopenharmony_ci "IPMI 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"); 42168c2ecf20Sopenharmony_ci return 0; 42178c2ecf20Sopenharmony_ci } 42188c2ecf20Sopenharmony_ci 42198c2ecf20Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 42208c2ecf20Sopenharmony_ci recv_msg->msgid = msg->msgid; 42218c2ecf20Sopenharmony_ci smi_addr = ((struct ipmi_system_interface_addr *) 42228c2ecf20Sopenharmony_ci &recv_msg->addr); 42238c2ecf20Sopenharmony_ci smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 42248c2ecf20Sopenharmony_ci smi_addr->channel = IPMI_BMC_CHANNEL; 42258c2ecf20Sopenharmony_ci smi_addr->lun = msg->rsp[0] & 3; 42268c2ecf20Sopenharmony_ci recv_msg->msg.netfn = msg->rsp[0] >> 2; 42278c2ecf20Sopenharmony_ci recv_msg->msg.cmd = msg->rsp[1]; 42288c2ecf20Sopenharmony_ci memcpy(recv_msg->msg_data, &msg->rsp[2], msg->rsp_size - 2); 42298c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 42308c2ecf20Sopenharmony_ci recv_msg->msg.data_len = msg->rsp_size - 2; 42318c2ecf20Sopenharmony_ci deliver_local_response(intf, recv_msg); 42328c2ecf20Sopenharmony_ci 42338c2ecf20Sopenharmony_ci return 0; 42348c2ecf20Sopenharmony_ci} 42358c2ecf20Sopenharmony_ci 42368c2ecf20Sopenharmony_ci/* 42378c2ecf20Sopenharmony_ci * Handle a received message. Return 1 if the message should be requeued, 42388c2ecf20Sopenharmony_ci * 0 if the message should be freed, or -1 if the message should not 42398c2ecf20Sopenharmony_ci * be freed or requeued. 42408c2ecf20Sopenharmony_ci */ 42418c2ecf20Sopenharmony_cistatic int handle_one_recv_msg(struct ipmi_smi *intf, 42428c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 42438c2ecf20Sopenharmony_ci{ 42448c2ecf20Sopenharmony_ci int requeue; 42458c2ecf20Sopenharmony_ci int chan; 42468c2ecf20Sopenharmony_ci 42478c2ecf20Sopenharmony_ci pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp); 42488c2ecf20Sopenharmony_ci 42498c2ecf20Sopenharmony_ci if ((msg->data_size >= 2) 42508c2ecf20Sopenharmony_ci && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) 42518c2ecf20Sopenharmony_ci && (msg->data[1] == IPMI_SEND_MSG_CMD) 42528c2ecf20Sopenharmony_ci && (msg->user_data == NULL)) { 42538c2ecf20Sopenharmony_ci 42548c2ecf20Sopenharmony_ci if (intf->in_shutdown) 42558c2ecf20Sopenharmony_ci goto free_msg; 42568c2ecf20Sopenharmony_ci 42578c2ecf20Sopenharmony_ci /* 42588c2ecf20Sopenharmony_ci * This is the local response to a command send, start 42598c2ecf20Sopenharmony_ci * the timer for these. The user_data will not be 42608c2ecf20Sopenharmony_ci * NULL if this is a response send, and we will let 42618c2ecf20Sopenharmony_ci * response sends just go through. 42628c2ecf20Sopenharmony_ci */ 42638c2ecf20Sopenharmony_ci 42648c2ecf20Sopenharmony_ci /* 42658c2ecf20Sopenharmony_ci * Check for errors, if we get certain errors (ones 42668c2ecf20Sopenharmony_ci * that mean basically we can try again later), we 42678c2ecf20Sopenharmony_ci * ignore them and start the timer. Otherwise we 42688c2ecf20Sopenharmony_ci * report the error immediately. 42698c2ecf20Sopenharmony_ci */ 42708c2ecf20Sopenharmony_ci if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0) 42718c2ecf20Sopenharmony_ci && (msg->rsp[2] != IPMI_NODE_BUSY_ERR) 42728c2ecf20Sopenharmony_ci && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR) 42738c2ecf20Sopenharmony_ci && (msg->rsp[2] != IPMI_BUS_ERR) 42748c2ecf20Sopenharmony_ci && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) { 42758c2ecf20Sopenharmony_ci int ch = msg->rsp[3] & 0xf; 42768c2ecf20Sopenharmony_ci struct ipmi_channel *chans; 42778c2ecf20Sopenharmony_ci 42788c2ecf20Sopenharmony_ci /* Got an error sending the message, handle it. */ 42798c2ecf20Sopenharmony_ci 42808c2ecf20Sopenharmony_ci chans = READ_ONCE(intf->channel_list)->c; 42818c2ecf20Sopenharmony_ci if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN) 42828c2ecf20Sopenharmony_ci || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC)) 42838c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_lan_command_errs); 42848c2ecf20Sopenharmony_ci else 42858c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, sent_ipmb_command_errs); 42868c2ecf20Sopenharmony_ci intf_err_seq(intf, msg->msgid, msg->rsp[2]); 42878c2ecf20Sopenharmony_ci } else 42888c2ecf20Sopenharmony_ci /* The message was sent, start the timer. */ 42898c2ecf20Sopenharmony_ci intf_start_seq_timer(intf, msg->msgid); 42908c2ecf20Sopenharmony_cifree_msg: 42918c2ecf20Sopenharmony_ci requeue = 0; 42928c2ecf20Sopenharmony_ci goto out; 42938c2ecf20Sopenharmony_ci 42948c2ecf20Sopenharmony_ci } else if (msg->rsp_size < 2) { 42958c2ecf20Sopenharmony_ci /* Message is too small to be correct. */ 42968c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 42978c2ecf20Sopenharmony_ci "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n", 42988c2ecf20Sopenharmony_ci (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size); 42998c2ecf20Sopenharmony_ci 43008c2ecf20Sopenharmony_ci /* Generate an error response for the message. */ 43018c2ecf20Sopenharmony_ci msg->rsp[0] = msg->data[0] | (1 << 2); 43028c2ecf20Sopenharmony_ci msg->rsp[1] = msg->data[1]; 43038c2ecf20Sopenharmony_ci msg->rsp[2] = IPMI_ERR_UNSPECIFIED; 43048c2ecf20Sopenharmony_ci msg->rsp_size = 3; 43058c2ecf20Sopenharmony_ci } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1)) 43068c2ecf20Sopenharmony_ci || (msg->rsp[1] != msg->data[1])) { 43078c2ecf20Sopenharmony_ci /* 43088c2ecf20Sopenharmony_ci * The NetFN and Command in the response is not even 43098c2ecf20Sopenharmony_ci * marginally correct. 43108c2ecf20Sopenharmony_ci */ 43118c2ecf20Sopenharmony_ci dev_warn(intf->si_dev, 43128c2ecf20Sopenharmony_ci "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n", 43138c2ecf20Sopenharmony_ci (msg->data[0] >> 2) | 1, msg->data[1], 43148c2ecf20Sopenharmony_ci msg->rsp[0] >> 2, msg->rsp[1]); 43158c2ecf20Sopenharmony_ci 43168c2ecf20Sopenharmony_ci /* Generate an error response for the message. */ 43178c2ecf20Sopenharmony_ci msg->rsp[0] = msg->data[0] | (1 << 2); 43188c2ecf20Sopenharmony_ci msg->rsp[1] = msg->data[1]; 43198c2ecf20Sopenharmony_ci msg->rsp[2] = IPMI_ERR_UNSPECIFIED; 43208c2ecf20Sopenharmony_ci msg->rsp_size = 3; 43218c2ecf20Sopenharmony_ci } 43228c2ecf20Sopenharmony_ci 43238c2ecf20Sopenharmony_ci if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) 43248c2ecf20Sopenharmony_ci && (msg->rsp[1] == IPMI_SEND_MSG_CMD) 43258c2ecf20Sopenharmony_ci && (msg->user_data != NULL)) { 43268c2ecf20Sopenharmony_ci /* 43278c2ecf20Sopenharmony_ci * It's a response to a response we sent. For this we 43288c2ecf20Sopenharmony_ci * deliver a send message response to the user. 43298c2ecf20Sopenharmony_ci */ 43308c2ecf20Sopenharmony_ci struct ipmi_recv_msg *recv_msg = msg->user_data; 43318c2ecf20Sopenharmony_ci 43328c2ecf20Sopenharmony_ci requeue = 0; 43338c2ecf20Sopenharmony_ci if (msg->rsp_size < 2) 43348c2ecf20Sopenharmony_ci /* Message is too small to be correct. */ 43358c2ecf20Sopenharmony_ci goto out; 43368c2ecf20Sopenharmony_ci 43378c2ecf20Sopenharmony_ci chan = msg->data[2] & 0x0f; 43388c2ecf20Sopenharmony_ci if (chan >= IPMI_MAX_CHANNELS) 43398c2ecf20Sopenharmony_ci /* Invalid channel number */ 43408c2ecf20Sopenharmony_ci goto out; 43418c2ecf20Sopenharmony_ci 43428c2ecf20Sopenharmony_ci if (!recv_msg) 43438c2ecf20Sopenharmony_ci goto out; 43448c2ecf20Sopenharmony_ci 43458c2ecf20Sopenharmony_ci recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE; 43468c2ecf20Sopenharmony_ci recv_msg->msg.data = recv_msg->msg_data; 43478c2ecf20Sopenharmony_ci recv_msg->msg.data_len = 1; 43488c2ecf20Sopenharmony_ci recv_msg->msg_data[0] = msg->rsp[2]; 43498c2ecf20Sopenharmony_ci deliver_local_response(intf, recv_msg); 43508c2ecf20Sopenharmony_ci } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) 43518c2ecf20Sopenharmony_ci && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { 43528c2ecf20Sopenharmony_ci struct ipmi_channel *chans; 43538c2ecf20Sopenharmony_ci 43548c2ecf20Sopenharmony_ci /* It's from the receive queue. */ 43558c2ecf20Sopenharmony_ci chan = msg->rsp[3] & 0xf; 43568c2ecf20Sopenharmony_ci if (chan >= IPMI_MAX_CHANNELS) { 43578c2ecf20Sopenharmony_ci /* Invalid channel number */ 43588c2ecf20Sopenharmony_ci requeue = 0; 43598c2ecf20Sopenharmony_ci goto out; 43608c2ecf20Sopenharmony_ci } 43618c2ecf20Sopenharmony_ci 43628c2ecf20Sopenharmony_ci /* 43638c2ecf20Sopenharmony_ci * We need to make sure the channels have been initialized. 43648c2ecf20Sopenharmony_ci * The channel_handler routine will set the "curr_channel" 43658c2ecf20Sopenharmony_ci * equal to or greater than IPMI_MAX_CHANNELS when all the 43668c2ecf20Sopenharmony_ci * channels for this interface have been initialized. 43678c2ecf20Sopenharmony_ci */ 43688c2ecf20Sopenharmony_ci if (!intf->channels_ready) { 43698c2ecf20Sopenharmony_ci requeue = 0; /* Throw the message away */ 43708c2ecf20Sopenharmony_ci goto out; 43718c2ecf20Sopenharmony_ci } 43728c2ecf20Sopenharmony_ci 43738c2ecf20Sopenharmony_ci chans = READ_ONCE(intf->channel_list)->c; 43748c2ecf20Sopenharmony_ci 43758c2ecf20Sopenharmony_ci switch (chans[chan].medium) { 43768c2ecf20Sopenharmony_ci case IPMI_CHANNEL_MEDIUM_IPMB: 43778c2ecf20Sopenharmony_ci if (msg->rsp[4] & 0x04) { 43788c2ecf20Sopenharmony_ci /* 43798c2ecf20Sopenharmony_ci * It's a response, so find the 43808c2ecf20Sopenharmony_ci * requesting message and send it up. 43818c2ecf20Sopenharmony_ci */ 43828c2ecf20Sopenharmony_ci requeue = handle_ipmb_get_msg_rsp(intf, msg); 43838c2ecf20Sopenharmony_ci } else { 43848c2ecf20Sopenharmony_ci /* 43858c2ecf20Sopenharmony_ci * It's a command to the SMS from some other 43868c2ecf20Sopenharmony_ci * entity. Handle that. 43878c2ecf20Sopenharmony_ci */ 43888c2ecf20Sopenharmony_ci requeue = handle_ipmb_get_msg_cmd(intf, msg); 43898c2ecf20Sopenharmony_ci } 43908c2ecf20Sopenharmony_ci break; 43918c2ecf20Sopenharmony_ci 43928c2ecf20Sopenharmony_ci case IPMI_CHANNEL_MEDIUM_8023LAN: 43938c2ecf20Sopenharmony_ci case IPMI_CHANNEL_MEDIUM_ASYNC: 43948c2ecf20Sopenharmony_ci if (msg->rsp[6] & 0x04) { 43958c2ecf20Sopenharmony_ci /* 43968c2ecf20Sopenharmony_ci * It's a response, so find the 43978c2ecf20Sopenharmony_ci * requesting message and send it up. 43988c2ecf20Sopenharmony_ci */ 43998c2ecf20Sopenharmony_ci requeue = handle_lan_get_msg_rsp(intf, msg); 44008c2ecf20Sopenharmony_ci } else { 44018c2ecf20Sopenharmony_ci /* 44028c2ecf20Sopenharmony_ci * It's a command to the SMS from some other 44038c2ecf20Sopenharmony_ci * entity. Handle that. 44048c2ecf20Sopenharmony_ci */ 44058c2ecf20Sopenharmony_ci requeue = handle_lan_get_msg_cmd(intf, msg); 44068c2ecf20Sopenharmony_ci } 44078c2ecf20Sopenharmony_ci break; 44088c2ecf20Sopenharmony_ci 44098c2ecf20Sopenharmony_ci default: 44108c2ecf20Sopenharmony_ci /* Check for OEM Channels. Clients had better 44118c2ecf20Sopenharmony_ci register for these commands. */ 44128c2ecf20Sopenharmony_ci if ((chans[chan].medium >= IPMI_CHANNEL_MEDIUM_OEM_MIN) 44138c2ecf20Sopenharmony_ci && (chans[chan].medium 44148c2ecf20Sopenharmony_ci <= IPMI_CHANNEL_MEDIUM_OEM_MAX)) { 44158c2ecf20Sopenharmony_ci requeue = handle_oem_get_msg_cmd(intf, msg); 44168c2ecf20Sopenharmony_ci } else { 44178c2ecf20Sopenharmony_ci /* 44188c2ecf20Sopenharmony_ci * We don't handle the channel type, so just 44198c2ecf20Sopenharmony_ci * free the message. 44208c2ecf20Sopenharmony_ci */ 44218c2ecf20Sopenharmony_ci requeue = 0; 44228c2ecf20Sopenharmony_ci } 44238c2ecf20Sopenharmony_ci } 44248c2ecf20Sopenharmony_ci 44258c2ecf20Sopenharmony_ci } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) 44268c2ecf20Sopenharmony_ci && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { 44278c2ecf20Sopenharmony_ci /* It's an asynchronous event. */ 44288c2ecf20Sopenharmony_ci requeue = handle_read_event_rsp(intf, msg); 44298c2ecf20Sopenharmony_ci } else { 44308c2ecf20Sopenharmony_ci /* It's a response from the local BMC. */ 44318c2ecf20Sopenharmony_ci requeue = handle_bmc_rsp(intf, msg); 44328c2ecf20Sopenharmony_ci } 44338c2ecf20Sopenharmony_ci 44348c2ecf20Sopenharmony_ci out: 44358c2ecf20Sopenharmony_ci return requeue; 44368c2ecf20Sopenharmony_ci} 44378c2ecf20Sopenharmony_ci 44388c2ecf20Sopenharmony_ci/* 44398c2ecf20Sopenharmony_ci * If there are messages in the queue or pretimeouts, handle them. 44408c2ecf20Sopenharmony_ci */ 44418c2ecf20Sopenharmony_cistatic void handle_new_recv_msgs(struct ipmi_smi *intf) 44428c2ecf20Sopenharmony_ci{ 44438c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg; 44448c2ecf20Sopenharmony_ci unsigned long flags = 0; 44458c2ecf20Sopenharmony_ci int rv; 44468c2ecf20Sopenharmony_ci int run_to_completion = intf->run_to_completion; 44478c2ecf20Sopenharmony_ci 44488c2ecf20Sopenharmony_ci /* See if any waiting messages need to be processed. */ 44498c2ecf20Sopenharmony_ci if (!run_to_completion) 44508c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); 44518c2ecf20Sopenharmony_ci while (!list_empty(&intf->waiting_rcv_msgs)) { 44528c2ecf20Sopenharmony_ci smi_msg = list_entry(intf->waiting_rcv_msgs.next, 44538c2ecf20Sopenharmony_ci struct ipmi_smi_msg, link); 44548c2ecf20Sopenharmony_ci list_del(&smi_msg->link); 44558c2ecf20Sopenharmony_ci if (!run_to_completion) 44568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, 44578c2ecf20Sopenharmony_ci flags); 44588c2ecf20Sopenharmony_ci rv = handle_one_recv_msg(intf, smi_msg); 44598c2ecf20Sopenharmony_ci if (!run_to_completion) 44608c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); 44618c2ecf20Sopenharmony_ci if (rv > 0) { 44628c2ecf20Sopenharmony_ci /* 44638c2ecf20Sopenharmony_ci * To preserve message order, quit if we 44648c2ecf20Sopenharmony_ci * can't handle a message. Add the message 44658c2ecf20Sopenharmony_ci * back at the head, this is safe because this 44668c2ecf20Sopenharmony_ci * tasklet is the only thing that pulls the 44678c2ecf20Sopenharmony_ci * messages. 44688c2ecf20Sopenharmony_ci */ 44698c2ecf20Sopenharmony_ci list_add(&smi_msg->link, &intf->waiting_rcv_msgs); 44708c2ecf20Sopenharmony_ci break; 44718c2ecf20Sopenharmony_ci } else { 44728c2ecf20Sopenharmony_ci if (rv == 0) 44738c2ecf20Sopenharmony_ci /* Message handled */ 44748c2ecf20Sopenharmony_ci ipmi_free_smi_msg(smi_msg); 44758c2ecf20Sopenharmony_ci /* If rv < 0, fatal error, del but don't free. */ 44768c2ecf20Sopenharmony_ci } 44778c2ecf20Sopenharmony_ci } 44788c2ecf20Sopenharmony_ci if (!run_to_completion) 44798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, flags); 44808c2ecf20Sopenharmony_ci 44818c2ecf20Sopenharmony_ci /* 44828c2ecf20Sopenharmony_ci * If the pretimout count is non-zero, decrement one from it and 44838c2ecf20Sopenharmony_ci * deliver pretimeouts to all the users. 44848c2ecf20Sopenharmony_ci */ 44858c2ecf20Sopenharmony_ci if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { 44868c2ecf20Sopenharmony_ci struct ipmi_user *user; 44878c2ecf20Sopenharmony_ci int index; 44888c2ecf20Sopenharmony_ci 44898c2ecf20Sopenharmony_ci index = srcu_read_lock(&intf->users_srcu); 44908c2ecf20Sopenharmony_ci list_for_each_entry_rcu(user, &intf->users, link) { 44918c2ecf20Sopenharmony_ci if (user->handler->ipmi_watchdog_pretimeout) 44928c2ecf20Sopenharmony_ci user->handler->ipmi_watchdog_pretimeout( 44938c2ecf20Sopenharmony_ci user->handler_data); 44948c2ecf20Sopenharmony_ci } 44958c2ecf20Sopenharmony_ci srcu_read_unlock(&intf->users_srcu, index); 44968c2ecf20Sopenharmony_ci } 44978c2ecf20Sopenharmony_ci} 44988c2ecf20Sopenharmony_ci 44998c2ecf20Sopenharmony_cistatic void smi_recv_tasklet(struct tasklet_struct *t) 45008c2ecf20Sopenharmony_ci{ 45018c2ecf20Sopenharmony_ci unsigned long flags = 0; /* keep us warning-free. */ 45028c2ecf20Sopenharmony_ci struct ipmi_smi *intf = from_tasklet(intf, t, recv_tasklet); 45038c2ecf20Sopenharmony_ci int run_to_completion = intf->run_to_completion; 45048c2ecf20Sopenharmony_ci struct ipmi_smi_msg *newmsg = NULL; 45058c2ecf20Sopenharmony_ci 45068c2ecf20Sopenharmony_ci /* 45078c2ecf20Sopenharmony_ci * Start the next message if available. 45088c2ecf20Sopenharmony_ci * 45098c2ecf20Sopenharmony_ci * Do this here, not in the actual receiver, because we may deadlock 45108c2ecf20Sopenharmony_ci * because the lower layer is allowed to hold locks while calling 45118c2ecf20Sopenharmony_ci * message delivery. 45128c2ecf20Sopenharmony_ci */ 45138c2ecf20Sopenharmony_ci 45148c2ecf20Sopenharmony_ci rcu_read_lock(); 45158c2ecf20Sopenharmony_ci 45168c2ecf20Sopenharmony_ci if (!run_to_completion) 45178c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->xmit_msgs_lock, flags); 45188c2ecf20Sopenharmony_ci if (intf->curr_msg == NULL && !intf->in_shutdown) { 45198c2ecf20Sopenharmony_ci struct list_head *entry = NULL; 45208c2ecf20Sopenharmony_ci 45218c2ecf20Sopenharmony_ci /* Pick the high priority queue first. */ 45228c2ecf20Sopenharmony_ci if (!list_empty(&intf->hp_xmit_msgs)) 45238c2ecf20Sopenharmony_ci entry = intf->hp_xmit_msgs.next; 45248c2ecf20Sopenharmony_ci else if (!list_empty(&intf->xmit_msgs)) 45258c2ecf20Sopenharmony_ci entry = intf->xmit_msgs.next; 45268c2ecf20Sopenharmony_ci 45278c2ecf20Sopenharmony_ci if (entry) { 45288c2ecf20Sopenharmony_ci list_del(entry); 45298c2ecf20Sopenharmony_ci newmsg = list_entry(entry, struct ipmi_smi_msg, link); 45308c2ecf20Sopenharmony_ci intf->curr_msg = newmsg; 45318c2ecf20Sopenharmony_ci } 45328c2ecf20Sopenharmony_ci } 45338c2ecf20Sopenharmony_ci 45348c2ecf20Sopenharmony_ci if (!run_to_completion) 45358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); 45368c2ecf20Sopenharmony_ci if (newmsg) 45378c2ecf20Sopenharmony_ci intf->handlers->sender(intf->send_info, newmsg); 45388c2ecf20Sopenharmony_ci 45398c2ecf20Sopenharmony_ci rcu_read_unlock(); 45408c2ecf20Sopenharmony_ci 45418c2ecf20Sopenharmony_ci handle_new_recv_msgs(intf); 45428c2ecf20Sopenharmony_ci} 45438c2ecf20Sopenharmony_ci 45448c2ecf20Sopenharmony_ci/* Handle a new message from the lower layer. */ 45458c2ecf20Sopenharmony_civoid ipmi_smi_msg_received(struct ipmi_smi *intf, 45468c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 45478c2ecf20Sopenharmony_ci{ 45488c2ecf20Sopenharmony_ci unsigned long flags = 0; /* keep us warning-free. */ 45498c2ecf20Sopenharmony_ci int run_to_completion = intf->run_to_completion; 45508c2ecf20Sopenharmony_ci 45518c2ecf20Sopenharmony_ci /* 45528c2ecf20Sopenharmony_ci * To preserve message order, we keep a queue and deliver from 45538c2ecf20Sopenharmony_ci * a tasklet. 45548c2ecf20Sopenharmony_ci */ 45558c2ecf20Sopenharmony_ci if (!run_to_completion) 45568c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); 45578c2ecf20Sopenharmony_ci list_add_tail(&msg->link, &intf->waiting_rcv_msgs); 45588c2ecf20Sopenharmony_ci if (!run_to_completion) 45598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, 45608c2ecf20Sopenharmony_ci flags); 45618c2ecf20Sopenharmony_ci 45628c2ecf20Sopenharmony_ci if (!run_to_completion) 45638c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->xmit_msgs_lock, flags); 45648c2ecf20Sopenharmony_ci /* 45658c2ecf20Sopenharmony_ci * We can get an asynchronous event or receive message in addition 45668c2ecf20Sopenharmony_ci * to commands we send. 45678c2ecf20Sopenharmony_ci */ 45688c2ecf20Sopenharmony_ci if (msg == intf->curr_msg) 45698c2ecf20Sopenharmony_ci intf->curr_msg = NULL; 45708c2ecf20Sopenharmony_ci if (!run_to_completion) 45718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); 45728c2ecf20Sopenharmony_ci 45738c2ecf20Sopenharmony_ci if (run_to_completion) 45748c2ecf20Sopenharmony_ci smi_recv_tasklet(&intf->recv_tasklet); 45758c2ecf20Sopenharmony_ci else 45768c2ecf20Sopenharmony_ci tasklet_schedule(&intf->recv_tasklet); 45778c2ecf20Sopenharmony_ci} 45788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_msg_received); 45798c2ecf20Sopenharmony_ci 45808c2ecf20Sopenharmony_civoid ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf) 45818c2ecf20Sopenharmony_ci{ 45828c2ecf20Sopenharmony_ci if (intf->in_shutdown) 45838c2ecf20Sopenharmony_ci return; 45848c2ecf20Sopenharmony_ci 45858c2ecf20Sopenharmony_ci atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); 45868c2ecf20Sopenharmony_ci tasklet_schedule(&intf->recv_tasklet); 45878c2ecf20Sopenharmony_ci} 45888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_cistatic struct ipmi_smi_msg * 45918c2ecf20Sopenharmony_cismi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg, 45928c2ecf20Sopenharmony_ci unsigned char seq, long seqid) 45938c2ecf20Sopenharmony_ci{ 45948c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg = ipmi_alloc_smi_msg(); 45958c2ecf20Sopenharmony_ci if (!smi_msg) 45968c2ecf20Sopenharmony_ci /* 45978c2ecf20Sopenharmony_ci * If we can't allocate the message, then just return, we 45988c2ecf20Sopenharmony_ci * get 4 retries, so this should be ok. 45998c2ecf20Sopenharmony_ci */ 46008c2ecf20Sopenharmony_ci return NULL; 46018c2ecf20Sopenharmony_ci 46028c2ecf20Sopenharmony_ci memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len); 46038c2ecf20Sopenharmony_ci smi_msg->data_size = recv_msg->msg.data_len; 46048c2ecf20Sopenharmony_ci smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid); 46058c2ecf20Sopenharmony_ci 46068c2ecf20Sopenharmony_ci pr_debug("Resend: %*ph\n", smi_msg->data_size, smi_msg->data); 46078c2ecf20Sopenharmony_ci 46088c2ecf20Sopenharmony_ci return smi_msg; 46098c2ecf20Sopenharmony_ci} 46108c2ecf20Sopenharmony_ci 46118c2ecf20Sopenharmony_cistatic void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, 46128c2ecf20Sopenharmony_ci struct list_head *timeouts, 46138c2ecf20Sopenharmony_ci unsigned long timeout_period, 46148c2ecf20Sopenharmony_ci int slot, unsigned long *flags, 46158c2ecf20Sopenharmony_ci bool *need_timer) 46168c2ecf20Sopenharmony_ci{ 46178c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg; 46188c2ecf20Sopenharmony_ci 46198c2ecf20Sopenharmony_ci if (intf->in_shutdown) 46208c2ecf20Sopenharmony_ci return; 46218c2ecf20Sopenharmony_ci 46228c2ecf20Sopenharmony_ci if (!ent->inuse) 46238c2ecf20Sopenharmony_ci return; 46248c2ecf20Sopenharmony_ci 46258c2ecf20Sopenharmony_ci if (timeout_period < ent->timeout) { 46268c2ecf20Sopenharmony_ci ent->timeout -= timeout_period; 46278c2ecf20Sopenharmony_ci *need_timer = true; 46288c2ecf20Sopenharmony_ci return; 46298c2ecf20Sopenharmony_ci } 46308c2ecf20Sopenharmony_ci 46318c2ecf20Sopenharmony_ci if (ent->retries_left == 0) { 46328c2ecf20Sopenharmony_ci /* The message has used all its retries. */ 46338c2ecf20Sopenharmony_ci ent->inuse = 0; 46348c2ecf20Sopenharmony_ci smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); 46358c2ecf20Sopenharmony_ci msg = ent->recv_msg; 46368c2ecf20Sopenharmony_ci list_add_tail(&msg->link, timeouts); 46378c2ecf20Sopenharmony_ci if (ent->broadcast) 46388c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, timed_out_ipmb_broadcasts); 46398c2ecf20Sopenharmony_ci else if (is_lan_addr(&ent->recv_msg->addr)) 46408c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, timed_out_lan_commands); 46418c2ecf20Sopenharmony_ci else 46428c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, timed_out_ipmb_commands); 46438c2ecf20Sopenharmony_ci } else { 46448c2ecf20Sopenharmony_ci struct ipmi_smi_msg *smi_msg; 46458c2ecf20Sopenharmony_ci /* More retries, send again. */ 46468c2ecf20Sopenharmony_ci 46478c2ecf20Sopenharmony_ci *need_timer = true; 46488c2ecf20Sopenharmony_ci 46498c2ecf20Sopenharmony_ci /* 46508c2ecf20Sopenharmony_ci * Start with the max timer, set to normal timer after 46518c2ecf20Sopenharmony_ci * the message is sent. 46528c2ecf20Sopenharmony_ci */ 46538c2ecf20Sopenharmony_ci ent->timeout = MAX_MSG_TIMEOUT; 46548c2ecf20Sopenharmony_ci ent->retries_left--; 46558c2ecf20Sopenharmony_ci smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot, 46568c2ecf20Sopenharmony_ci ent->seqid); 46578c2ecf20Sopenharmony_ci if (!smi_msg) { 46588c2ecf20Sopenharmony_ci if (is_lan_addr(&ent->recv_msg->addr)) 46598c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, 46608c2ecf20Sopenharmony_ci dropped_rexmit_lan_commands); 46618c2ecf20Sopenharmony_ci else 46628c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, 46638c2ecf20Sopenharmony_ci dropped_rexmit_ipmb_commands); 46648c2ecf20Sopenharmony_ci return; 46658c2ecf20Sopenharmony_ci } 46668c2ecf20Sopenharmony_ci 46678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, *flags); 46688c2ecf20Sopenharmony_ci 46698c2ecf20Sopenharmony_ci /* 46708c2ecf20Sopenharmony_ci * Send the new message. We send with a zero 46718c2ecf20Sopenharmony_ci * priority. It timed out, I doubt time is that 46728c2ecf20Sopenharmony_ci * critical now, and high priority messages are really 46738c2ecf20Sopenharmony_ci * only for messages to the local MC, which don't get 46748c2ecf20Sopenharmony_ci * resent. 46758c2ecf20Sopenharmony_ci */ 46768c2ecf20Sopenharmony_ci if (intf->handlers) { 46778c2ecf20Sopenharmony_ci if (is_lan_addr(&ent->recv_msg->addr)) 46788c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, 46798c2ecf20Sopenharmony_ci retransmitted_lan_commands); 46808c2ecf20Sopenharmony_ci else 46818c2ecf20Sopenharmony_ci ipmi_inc_stat(intf, 46828c2ecf20Sopenharmony_ci retransmitted_ipmb_commands); 46838c2ecf20Sopenharmony_ci 46848c2ecf20Sopenharmony_ci smi_send(intf, intf->handlers, smi_msg, 0); 46858c2ecf20Sopenharmony_ci } else 46868c2ecf20Sopenharmony_ci ipmi_free_smi_msg(smi_msg); 46878c2ecf20Sopenharmony_ci 46888c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, *flags); 46898c2ecf20Sopenharmony_ci } 46908c2ecf20Sopenharmony_ci} 46918c2ecf20Sopenharmony_ci 46928c2ecf20Sopenharmony_cistatic bool ipmi_timeout_handler(struct ipmi_smi *intf, 46938c2ecf20Sopenharmony_ci unsigned long timeout_period) 46948c2ecf20Sopenharmony_ci{ 46958c2ecf20Sopenharmony_ci struct list_head timeouts; 46968c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg, *msg2; 46978c2ecf20Sopenharmony_ci unsigned long flags; 46988c2ecf20Sopenharmony_ci int i; 46998c2ecf20Sopenharmony_ci bool need_timer = false; 47008c2ecf20Sopenharmony_ci 47018c2ecf20Sopenharmony_ci if (!intf->bmc_registered) { 47028c2ecf20Sopenharmony_ci kref_get(&intf->refcount); 47038c2ecf20Sopenharmony_ci if (!schedule_work(&intf->bmc_reg_work)) { 47048c2ecf20Sopenharmony_ci kref_put(&intf->refcount, intf_free); 47058c2ecf20Sopenharmony_ci need_timer = true; 47068c2ecf20Sopenharmony_ci } 47078c2ecf20Sopenharmony_ci } 47088c2ecf20Sopenharmony_ci 47098c2ecf20Sopenharmony_ci /* 47108c2ecf20Sopenharmony_ci * Go through the seq table and find any messages that 47118c2ecf20Sopenharmony_ci * have timed out, putting them in the timeouts 47128c2ecf20Sopenharmony_ci * list. 47138c2ecf20Sopenharmony_ci */ 47148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&timeouts); 47158c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->seq_lock, flags); 47168c2ecf20Sopenharmony_ci if (intf->ipmb_maintenance_mode_timeout) { 47178c2ecf20Sopenharmony_ci if (intf->ipmb_maintenance_mode_timeout <= timeout_period) 47188c2ecf20Sopenharmony_ci intf->ipmb_maintenance_mode_timeout = 0; 47198c2ecf20Sopenharmony_ci else 47208c2ecf20Sopenharmony_ci intf->ipmb_maintenance_mode_timeout -= timeout_period; 47218c2ecf20Sopenharmony_ci } 47228c2ecf20Sopenharmony_ci for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) 47238c2ecf20Sopenharmony_ci check_msg_timeout(intf, &intf->seq_table[i], 47248c2ecf20Sopenharmony_ci &timeouts, timeout_period, i, 47258c2ecf20Sopenharmony_ci &flags, &need_timer); 47268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->seq_lock, flags); 47278c2ecf20Sopenharmony_ci 47288c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg, msg2, &timeouts, link) 47298c2ecf20Sopenharmony_ci deliver_err_response(intf, msg, IPMI_TIMEOUT_COMPLETION_CODE); 47308c2ecf20Sopenharmony_ci 47318c2ecf20Sopenharmony_ci /* 47328c2ecf20Sopenharmony_ci * Maintenance mode handling. Check the timeout 47338c2ecf20Sopenharmony_ci * optimistically before we claim the lock. It may 47348c2ecf20Sopenharmony_ci * mean a timeout gets missed occasionally, but that 47358c2ecf20Sopenharmony_ci * only means the timeout gets extended by one period 47368c2ecf20Sopenharmony_ci * in that case. No big deal, and it avoids the lock 47378c2ecf20Sopenharmony_ci * most of the time. 47388c2ecf20Sopenharmony_ci */ 47398c2ecf20Sopenharmony_ci if (intf->auto_maintenance_timeout > 0) { 47408c2ecf20Sopenharmony_ci spin_lock_irqsave(&intf->maintenance_mode_lock, flags); 47418c2ecf20Sopenharmony_ci if (intf->auto_maintenance_timeout > 0) { 47428c2ecf20Sopenharmony_ci intf->auto_maintenance_timeout 47438c2ecf20Sopenharmony_ci -= timeout_period; 47448c2ecf20Sopenharmony_ci if (!intf->maintenance_mode 47458c2ecf20Sopenharmony_ci && (intf->auto_maintenance_timeout <= 0)) { 47468c2ecf20Sopenharmony_ci intf->maintenance_mode_enable = false; 47478c2ecf20Sopenharmony_ci maintenance_mode_update(intf); 47488c2ecf20Sopenharmony_ci } 47498c2ecf20Sopenharmony_ci } 47508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&intf->maintenance_mode_lock, 47518c2ecf20Sopenharmony_ci flags); 47528c2ecf20Sopenharmony_ci } 47538c2ecf20Sopenharmony_ci 47548c2ecf20Sopenharmony_ci tasklet_schedule(&intf->recv_tasklet); 47558c2ecf20Sopenharmony_ci 47568c2ecf20Sopenharmony_ci return need_timer; 47578c2ecf20Sopenharmony_ci} 47588c2ecf20Sopenharmony_ci 47598c2ecf20Sopenharmony_cistatic void ipmi_request_event(struct ipmi_smi *intf) 47608c2ecf20Sopenharmony_ci{ 47618c2ecf20Sopenharmony_ci /* No event requests when in maintenance mode. */ 47628c2ecf20Sopenharmony_ci if (intf->maintenance_mode_enable) 47638c2ecf20Sopenharmony_ci return; 47648c2ecf20Sopenharmony_ci 47658c2ecf20Sopenharmony_ci if (!intf->in_shutdown) 47668c2ecf20Sopenharmony_ci intf->handlers->request_events(intf->send_info); 47678c2ecf20Sopenharmony_ci} 47688c2ecf20Sopenharmony_ci 47698c2ecf20Sopenharmony_cistatic struct timer_list ipmi_timer; 47708c2ecf20Sopenharmony_ci 47718c2ecf20Sopenharmony_cistatic atomic_t stop_operation; 47728c2ecf20Sopenharmony_ci 47738c2ecf20Sopenharmony_cistatic void ipmi_timeout(struct timer_list *unused) 47748c2ecf20Sopenharmony_ci{ 47758c2ecf20Sopenharmony_ci struct ipmi_smi *intf; 47768c2ecf20Sopenharmony_ci bool need_timer = false; 47778c2ecf20Sopenharmony_ci int index; 47788c2ecf20Sopenharmony_ci 47798c2ecf20Sopenharmony_ci if (atomic_read(&stop_operation)) 47808c2ecf20Sopenharmony_ci return; 47818c2ecf20Sopenharmony_ci 47828c2ecf20Sopenharmony_ci index = srcu_read_lock(&ipmi_interfaces_srcu); 47838c2ecf20Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 47848c2ecf20Sopenharmony_ci if (atomic_read(&intf->event_waiters)) { 47858c2ecf20Sopenharmony_ci intf->ticks_to_req_ev--; 47868c2ecf20Sopenharmony_ci if (intf->ticks_to_req_ev == 0) { 47878c2ecf20Sopenharmony_ci ipmi_request_event(intf); 47888c2ecf20Sopenharmony_ci intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; 47898c2ecf20Sopenharmony_ci } 47908c2ecf20Sopenharmony_ci need_timer = true; 47918c2ecf20Sopenharmony_ci } 47928c2ecf20Sopenharmony_ci 47938c2ecf20Sopenharmony_ci need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); 47948c2ecf20Sopenharmony_ci } 47958c2ecf20Sopenharmony_ci srcu_read_unlock(&ipmi_interfaces_srcu, index); 47968c2ecf20Sopenharmony_ci 47978c2ecf20Sopenharmony_ci if (need_timer) 47988c2ecf20Sopenharmony_ci mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); 47998c2ecf20Sopenharmony_ci} 48008c2ecf20Sopenharmony_ci 48018c2ecf20Sopenharmony_cistatic void need_waiter(struct ipmi_smi *intf) 48028c2ecf20Sopenharmony_ci{ 48038c2ecf20Sopenharmony_ci /* Racy, but worst case we start the timer twice. */ 48048c2ecf20Sopenharmony_ci if (!timer_pending(&ipmi_timer)) 48058c2ecf20Sopenharmony_ci mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); 48068c2ecf20Sopenharmony_ci} 48078c2ecf20Sopenharmony_ci 48088c2ecf20Sopenharmony_cistatic atomic_t smi_msg_inuse_count = ATOMIC_INIT(0); 48098c2ecf20Sopenharmony_cistatic atomic_t recv_msg_inuse_count = ATOMIC_INIT(0); 48108c2ecf20Sopenharmony_ci 48118c2ecf20Sopenharmony_cistatic void free_smi_msg(struct ipmi_smi_msg *msg) 48128c2ecf20Sopenharmony_ci{ 48138c2ecf20Sopenharmony_ci atomic_dec(&smi_msg_inuse_count); 48148c2ecf20Sopenharmony_ci /* Try to keep as much stuff out of the panic path as possible. */ 48158c2ecf20Sopenharmony_ci if (!oops_in_progress) 48168c2ecf20Sopenharmony_ci kfree(msg); 48178c2ecf20Sopenharmony_ci} 48188c2ecf20Sopenharmony_ci 48198c2ecf20Sopenharmony_cistruct ipmi_smi_msg *ipmi_alloc_smi_msg(void) 48208c2ecf20Sopenharmony_ci{ 48218c2ecf20Sopenharmony_ci struct ipmi_smi_msg *rv; 48228c2ecf20Sopenharmony_ci rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC); 48238c2ecf20Sopenharmony_ci if (rv) { 48248c2ecf20Sopenharmony_ci rv->done = free_smi_msg; 48258c2ecf20Sopenharmony_ci rv->user_data = NULL; 48268c2ecf20Sopenharmony_ci atomic_inc(&smi_msg_inuse_count); 48278c2ecf20Sopenharmony_ci } 48288c2ecf20Sopenharmony_ci return rv; 48298c2ecf20Sopenharmony_ci} 48308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_alloc_smi_msg); 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_cistatic void free_recv_msg(struct ipmi_recv_msg *msg) 48338c2ecf20Sopenharmony_ci{ 48348c2ecf20Sopenharmony_ci atomic_dec(&recv_msg_inuse_count); 48358c2ecf20Sopenharmony_ci /* Try to keep as much stuff out of the panic path as possible. */ 48368c2ecf20Sopenharmony_ci if (!oops_in_progress) 48378c2ecf20Sopenharmony_ci kfree(msg); 48388c2ecf20Sopenharmony_ci} 48398c2ecf20Sopenharmony_ci 48408c2ecf20Sopenharmony_cistatic struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) 48418c2ecf20Sopenharmony_ci{ 48428c2ecf20Sopenharmony_ci struct ipmi_recv_msg *rv; 48438c2ecf20Sopenharmony_ci 48448c2ecf20Sopenharmony_ci rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC); 48458c2ecf20Sopenharmony_ci if (rv) { 48468c2ecf20Sopenharmony_ci rv->user = NULL; 48478c2ecf20Sopenharmony_ci rv->done = free_recv_msg; 48488c2ecf20Sopenharmony_ci atomic_inc(&recv_msg_inuse_count); 48498c2ecf20Sopenharmony_ci } 48508c2ecf20Sopenharmony_ci return rv; 48518c2ecf20Sopenharmony_ci} 48528c2ecf20Sopenharmony_ci 48538c2ecf20Sopenharmony_civoid ipmi_free_recv_msg(struct ipmi_recv_msg *msg) 48548c2ecf20Sopenharmony_ci{ 48558c2ecf20Sopenharmony_ci if (msg->user && !oops_in_progress) 48568c2ecf20Sopenharmony_ci kref_put(&msg->user->refcount, free_user); 48578c2ecf20Sopenharmony_ci msg->done(msg); 48588c2ecf20Sopenharmony_ci} 48598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_free_recv_msg); 48608c2ecf20Sopenharmony_ci 48618c2ecf20Sopenharmony_cistatic atomic_t panic_done_count = ATOMIC_INIT(0); 48628c2ecf20Sopenharmony_ci 48638c2ecf20Sopenharmony_cistatic void dummy_smi_done_handler(struct ipmi_smi_msg *msg) 48648c2ecf20Sopenharmony_ci{ 48658c2ecf20Sopenharmony_ci atomic_dec(&panic_done_count); 48668c2ecf20Sopenharmony_ci} 48678c2ecf20Sopenharmony_ci 48688c2ecf20Sopenharmony_cistatic void dummy_recv_done_handler(struct ipmi_recv_msg *msg) 48698c2ecf20Sopenharmony_ci{ 48708c2ecf20Sopenharmony_ci atomic_dec(&panic_done_count); 48718c2ecf20Sopenharmony_ci} 48728c2ecf20Sopenharmony_ci 48738c2ecf20Sopenharmony_ci/* 48748c2ecf20Sopenharmony_ci * Inside a panic, send a message and wait for a response. 48758c2ecf20Sopenharmony_ci */ 48768c2ecf20Sopenharmony_cistatic void ipmi_panic_request_and_wait(struct ipmi_smi *intf, 48778c2ecf20Sopenharmony_ci struct ipmi_addr *addr, 48788c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg) 48798c2ecf20Sopenharmony_ci{ 48808c2ecf20Sopenharmony_ci struct ipmi_smi_msg smi_msg; 48818c2ecf20Sopenharmony_ci struct ipmi_recv_msg recv_msg; 48828c2ecf20Sopenharmony_ci int rv; 48838c2ecf20Sopenharmony_ci 48848c2ecf20Sopenharmony_ci smi_msg.done = dummy_smi_done_handler; 48858c2ecf20Sopenharmony_ci recv_msg.done = dummy_recv_done_handler; 48868c2ecf20Sopenharmony_ci atomic_add(2, &panic_done_count); 48878c2ecf20Sopenharmony_ci rv = i_ipmi_request(NULL, 48888c2ecf20Sopenharmony_ci intf, 48898c2ecf20Sopenharmony_ci addr, 48908c2ecf20Sopenharmony_ci 0, 48918c2ecf20Sopenharmony_ci msg, 48928c2ecf20Sopenharmony_ci intf, 48938c2ecf20Sopenharmony_ci &smi_msg, 48948c2ecf20Sopenharmony_ci &recv_msg, 48958c2ecf20Sopenharmony_ci 0, 48968c2ecf20Sopenharmony_ci intf->addrinfo[0].address, 48978c2ecf20Sopenharmony_ci intf->addrinfo[0].lun, 48988c2ecf20Sopenharmony_ci 0, 1); /* Don't retry, and don't wait. */ 48998c2ecf20Sopenharmony_ci if (rv) 49008c2ecf20Sopenharmony_ci atomic_sub(2, &panic_done_count); 49018c2ecf20Sopenharmony_ci else if (intf->handlers->flush_messages) 49028c2ecf20Sopenharmony_ci intf->handlers->flush_messages(intf->send_info); 49038c2ecf20Sopenharmony_ci 49048c2ecf20Sopenharmony_ci while (atomic_read(&panic_done_count) != 0) 49058c2ecf20Sopenharmony_ci ipmi_poll(intf); 49068c2ecf20Sopenharmony_ci} 49078c2ecf20Sopenharmony_ci 49088c2ecf20Sopenharmony_cistatic void event_receiver_fetcher(struct ipmi_smi *intf, 49098c2ecf20Sopenharmony_ci struct ipmi_recv_msg *msg) 49108c2ecf20Sopenharmony_ci{ 49118c2ecf20Sopenharmony_ci if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 49128c2ecf20Sopenharmony_ci && (msg->msg.netfn == IPMI_NETFN_SENSOR_EVENT_RESPONSE) 49138c2ecf20Sopenharmony_ci && (msg->msg.cmd == IPMI_GET_EVENT_RECEIVER_CMD) 49148c2ecf20Sopenharmony_ci && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { 49158c2ecf20Sopenharmony_ci /* A get event receiver command, save it. */ 49168c2ecf20Sopenharmony_ci intf->event_receiver = msg->msg.data[1]; 49178c2ecf20Sopenharmony_ci intf->event_receiver_lun = msg->msg.data[2] & 0x3; 49188c2ecf20Sopenharmony_ci } 49198c2ecf20Sopenharmony_ci} 49208c2ecf20Sopenharmony_ci 49218c2ecf20Sopenharmony_cistatic void device_id_fetcher(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) 49228c2ecf20Sopenharmony_ci{ 49238c2ecf20Sopenharmony_ci if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) 49248c2ecf20Sopenharmony_ci && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) 49258c2ecf20Sopenharmony_ci && (msg->msg.cmd == IPMI_GET_DEVICE_ID_CMD) 49268c2ecf20Sopenharmony_ci && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { 49278c2ecf20Sopenharmony_ci /* 49288c2ecf20Sopenharmony_ci * A get device id command, save if we are an event 49298c2ecf20Sopenharmony_ci * receiver or generator. 49308c2ecf20Sopenharmony_ci */ 49318c2ecf20Sopenharmony_ci intf->local_sel_device = (msg->msg.data[6] >> 2) & 1; 49328c2ecf20Sopenharmony_ci intf->local_event_generator = (msg->msg.data[6] >> 5) & 1; 49338c2ecf20Sopenharmony_ci } 49348c2ecf20Sopenharmony_ci} 49358c2ecf20Sopenharmony_ci 49368c2ecf20Sopenharmony_cistatic void send_panic_events(struct ipmi_smi *intf, char *str) 49378c2ecf20Sopenharmony_ci{ 49388c2ecf20Sopenharmony_ci struct kernel_ipmi_msg msg; 49398c2ecf20Sopenharmony_ci unsigned char data[16]; 49408c2ecf20Sopenharmony_ci struct ipmi_system_interface_addr *si; 49418c2ecf20Sopenharmony_ci struct ipmi_addr addr; 49428c2ecf20Sopenharmony_ci char *p = str; 49438c2ecf20Sopenharmony_ci struct ipmi_ipmb_addr *ipmb; 49448c2ecf20Sopenharmony_ci int j; 49458c2ecf20Sopenharmony_ci 49468c2ecf20Sopenharmony_ci if (ipmi_send_panic_event == IPMI_SEND_PANIC_EVENT_NONE) 49478c2ecf20Sopenharmony_ci return; 49488c2ecf20Sopenharmony_ci 49498c2ecf20Sopenharmony_ci si = (struct ipmi_system_interface_addr *) &addr; 49508c2ecf20Sopenharmony_ci si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 49518c2ecf20Sopenharmony_ci si->channel = IPMI_BMC_CHANNEL; 49528c2ecf20Sopenharmony_ci si->lun = 0; 49538c2ecf20Sopenharmony_ci 49548c2ecf20Sopenharmony_ci /* Fill in an event telling that we have failed. */ 49558c2ecf20Sopenharmony_ci msg.netfn = 0x04; /* Sensor or Event. */ 49568c2ecf20Sopenharmony_ci msg.cmd = 2; /* Platform event command. */ 49578c2ecf20Sopenharmony_ci msg.data = data; 49588c2ecf20Sopenharmony_ci msg.data_len = 8; 49598c2ecf20Sopenharmony_ci data[0] = 0x41; /* Kernel generator ID, IPMI table 5-4 */ 49608c2ecf20Sopenharmony_ci data[1] = 0x03; /* This is for IPMI 1.0. */ 49618c2ecf20Sopenharmony_ci data[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */ 49628c2ecf20Sopenharmony_ci data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */ 49638c2ecf20Sopenharmony_ci data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */ 49648c2ecf20Sopenharmony_ci 49658c2ecf20Sopenharmony_ci /* 49668c2ecf20Sopenharmony_ci * Put a few breadcrumbs in. Hopefully later we can add more things 49678c2ecf20Sopenharmony_ci * to make the panic events more useful. 49688c2ecf20Sopenharmony_ci */ 49698c2ecf20Sopenharmony_ci if (str) { 49708c2ecf20Sopenharmony_ci data[3] = str[0]; 49718c2ecf20Sopenharmony_ci data[6] = str[1]; 49728c2ecf20Sopenharmony_ci data[7] = str[2]; 49738c2ecf20Sopenharmony_ci } 49748c2ecf20Sopenharmony_ci 49758c2ecf20Sopenharmony_ci /* Send the event announcing the panic. */ 49768c2ecf20Sopenharmony_ci ipmi_panic_request_and_wait(intf, &addr, &msg); 49778c2ecf20Sopenharmony_ci 49788c2ecf20Sopenharmony_ci /* 49798c2ecf20Sopenharmony_ci * On every interface, dump a bunch of OEM event holding the 49808c2ecf20Sopenharmony_ci * string. 49818c2ecf20Sopenharmony_ci */ 49828c2ecf20Sopenharmony_ci if (ipmi_send_panic_event != IPMI_SEND_PANIC_EVENT_STRING || !str) 49838c2ecf20Sopenharmony_ci return; 49848c2ecf20Sopenharmony_ci 49858c2ecf20Sopenharmony_ci /* 49868c2ecf20Sopenharmony_ci * intf_num is used as an marker to tell if the 49878c2ecf20Sopenharmony_ci * interface is valid. Thus we need a read barrier to 49888c2ecf20Sopenharmony_ci * make sure data fetched before checking intf_num 49898c2ecf20Sopenharmony_ci * won't be used. 49908c2ecf20Sopenharmony_ci */ 49918c2ecf20Sopenharmony_ci smp_rmb(); 49928c2ecf20Sopenharmony_ci 49938c2ecf20Sopenharmony_ci /* 49948c2ecf20Sopenharmony_ci * First job here is to figure out where to send the 49958c2ecf20Sopenharmony_ci * OEM events. There's no way in IPMI to send OEM 49968c2ecf20Sopenharmony_ci * events using an event send command, so we have to 49978c2ecf20Sopenharmony_ci * find the SEL to put them in and stick them in 49988c2ecf20Sopenharmony_ci * there. 49998c2ecf20Sopenharmony_ci */ 50008c2ecf20Sopenharmony_ci 50018c2ecf20Sopenharmony_ci /* Get capabilities from the get device id. */ 50028c2ecf20Sopenharmony_ci intf->local_sel_device = 0; 50038c2ecf20Sopenharmony_ci intf->local_event_generator = 0; 50048c2ecf20Sopenharmony_ci intf->event_receiver = 0; 50058c2ecf20Sopenharmony_ci 50068c2ecf20Sopenharmony_ci /* Request the device info from the local MC. */ 50078c2ecf20Sopenharmony_ci msg.netfn = IPMI_NETFN_APP_REQUEST; 50088c2ecf20Sopenharmony_ci msg.cmd = IPMI_GET_DEVICE_ID_CMD; 50098c2ecf20Sopenharmony_ci msg.data = NULL; 50108c2ecf20Sopenharmony_ci msg.data_len = 0; 50118c2ecf20Sopenharmony_ci intf->null_user_handler = device_id_fetcher; 50128c2ecf20Sopenharmony_ci ipmi_panic_request_and_wait(intf, &addr, &msg); 50138c2ecf20Sopenharmony_ci 50148c2ecf20Sopenharmony_ci if (intf->local_event_generator) { 50158c2ecf20Sopenharmony_ci /* Request the event receiver from the local MC. */ 50168c2ecf20Sopenharmony_ci msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST; 50178c2ecf20Sopenharmony_ci msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD; 50188c2ecf20Sopenharmony_ci msg.data = NULL; 50198c2ecf20Sopenharmony_ci msg.data_len = 0; 50208c2ecf20Sopenharmony_ci intf->null_user_handler = event_receiver_fetcher; 50218c2ecf20Sopenharmony_ci ipmi_panic_request_and_wait(intf, &addr, &msg); 50228c2ecf20Sopenharmony_ci } 50238c2ecf20Sopenharmony_ci intf->null_user_handler = NULL; 50248c2ecf20Sopenharmony_ci 50258c2ecf20Sopenharmony_ci /* 50268c2ecf20Sopenharmony_ci * Validate the event receiver. The low bit must not 50278c2ecf20Sopenharmony_ci * be 1 (it must be a valid IPMB address), it cannot 50288c2ecf20Sopenharmony_ci * be zero, and it must not be my address. 50298c2ecf20Sopenharmony_ci */ 50308c2ecf20Sopenharmony_ci if (((intf->event_receiver & 1) == 0) 50318c2ecf20Sopenharmony_ci && (intf->event_receiver != 0) 50328c2ecf20Sopenharmony_ci && (intf->event_receiver != intf->addrinfo[0].address)) { 50338c2ecf20Sopenharmony_ci /* 50348c2ecf20Sopenharmony_ci * The event receiver is valid, send an IPMB 50358c2ecf20Sopenharmony_ci * message. 50368c2ecf20Sopenharmony_ci */ 50378c2ecf20Sopenharmony_ci ipmb = (struct ipmi_ipmb_addr *) &addr; 50388c2ecf20Sopenharmony_ci ipmb->addr_type = IPMI_IPMB_ADDR_TYPE; 50398c2ecf20Sopenharmony_ci ipmb->channel = 0; /* FIXME - is this right? */ 50408c2ecf20Sopenharmony_ci ipmb->lun = intf->event_receiver_lun; 50418c2ecf20Sopenharmony_ci ipmb->slave_addr = intf->event_receiver; 50428c2ecf20Sopenharmony_ci } else if (intf->local_sel_device) { 50438c2ecf20Sopenharmony_ci /* 50448c2ecf20Sopenharmony_ci * The event receiver was not valid (or was 50458c2ecf20Sopenharmony_ci * me), but I am an SEL device, just dump it 50468c2ecf20Sopenharmony_ci * in my SEL. 50478c2ecf20Sopenharmony_ci */ 50488c2ecf20Sopenharmony_ci si = (struct ipmi_system_interface_addr *) &addr; 50498c2ecf20Sopenharmony_ci si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 50508c2ecf20Sopenharmony_ci si->channel = IPMI_BMC_CHANNEL; 50518c2ecf20Sopenharmony_ci si->lun = 0; 50528c2ecf20Sopenharmony_ci } else 50538c2ecf20Sopenharmony_ci return; /* No where to send the event. */ 50548c2ecf20Sopenharmony_ci 50558c2ecf20Sopenharmony_ci msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */ 50568c2ecf20Sopenharmony_ci msg.cmd = IPMI_ADD_SEL_ENTRY_CMD; 50578c2ecf20Sopenharmony_ci msg.data = data; 50588c2ecf20Sopenharmony_ci msg.data_len = 16; 50598c2ecf20Sopenharmony_ci 50608c2ecf20Sopenharmony_ci j = 0; 50618c2ecf20Sopenharmony_ci while (*p) { 50628c2ecf20Sopenharmony_ci int size = strlen(p); 50638c2ecf20Sopenharmony_ci 50648c2ecf20Sopenharmony_ci if (size > 11) 50658c2ecf20Sopenharmony_ci size = 11; 50668c2ecf20Sopenharmony_ci data[0] = 0; 50678c2ecf20Sopenharmony_ci data[1] = 0; 50688c2ecf20Sopenharmony_ci data[2] = 0xf0; /* OEM event without timestamp. */ 50698c2ecf20Sopenharmony_ci data[3] = intf->addrinfo[0].address; 50708c2ecf20Sopenharmony_ci data[4] = j++; /* sequence # */ 50718c2ecf20Sopenharmony_ci /* 50728c2ecf20Sopenharmony_ci * Always give 11 bytes, so strncpy will fill 50738c2ecf20Sopenharmony_ci * it with zeroes for me. 50748c2ecf20Sopenharmony_ci */ 50758c2ecf20Sopenharmony_ci strncpy(data+5, p, 11); 50768c2ecf20Sopenharmony_ci p += size; 50778c2ecf20Sopenharmony_ci 50788c2ecf20Sopenharmony_ci ipmi_panic_request_and_wait(intf, &addr, &msg); 50798c2ecf20Sopenharmony_ci } 50808c2ecf20Sopenharmony_ci} 50818c2ecf20Sopenharmony_ci 50828c2ecf20Sopenharmony_cistatic int has_panicked; 50838c2ecf20Sopenharmony_ci 50848c2ecf20Sopenharmony_cistatic int panic_event(struct notifier_block *this, 50858c2ecf20Sopenharmony_ci unsigned long event, 50868c2ecf20Sopenharmony_ci void *ptr) 50878c2ecf20Sopenharmony_ci{ 50888c2ecf20Sopenharmony_ci struct ipmi_smi *intf; 50898c2ecf20Sopenharmony_ci struct ipmi_user *user; 50908c2ecf20Sopenharmony_ci 50918c2ecf20Sopenharmony_ci if (has_panicked) 50928c2ecf20Sopenharmony_ci return NOTIFY_DONE; 50938c2ecf20Sopenharmony_ci has_panicked = 1; 50948c2ecf20Sopenharmony_ci 50958c2ecf20Sopenharmony_ci /* For every registered interface, set it to run to completion. */ 50968c2ecf20Sopenharmony_ci list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { 50978c2ecf20Sopenharmony_ci if (!intf->handlers || intf->intf_num == -1) 50988c2ecf20Sopenharmony_ci /* Interface is not ready. */ 50998c2ecf20Sopenharmony_ci continue; 51008c2ecf20Sopenharmony_ci 51018c2ecf20Sopenharmony_ci if (!intf->handlers->poll) 51028c2ecf20Sopenharmony_ci continue; 51038c2ecf20Sopenharmony_ci 51048c2ecf20Sopenharmony_ci /* 51058c2ecf20Sopenharmony_ci * If we were interrupted while locking xmit_msgs_lock or 51068c2ecf20Sopenharmony_ci * waiting_rcv_msgs_lock, the corresponding list may be 51078c2ecf20Sopenharmony_ci * corrupted. In this case, drop items on the list for 51088c2ecf20Sopenharmony_ci * the safety. 51098c2ecf20Sopenharmony_ci */ 51108c2ecf20Sopenharmony_ci if (!spin_trylock(&intf->xmit_msgs_lock)) { 51118c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->xmit_msgs); 51128c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->hp_xmit_msgs); 51138c2ecf20Sopenharmony_ci } else 51148c2ecf20Sopenharmony_ci spin_unlock(&intf->xmit_msgs_lock); 51158c2ecf20Sopenharmony_ci 51168c2ecf20Sopenharmony_ci if (!spin_trylock(&intf->waiting_rcv_msgs_lock)) 51178c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->waiting_rcv_msgs); 51188c2ecf20Sopenharmony_ci else 51198c2ecf20Sopenharmony_ci spin_unlock(&intf->waiting_rcv_msgs_lock); 51208c2ecf20Sopenharmony_ci 51218c2ecf20Sopenharmony_ci intf->run_to_completion = 1; 51228c2ecf20Sopenharmony_ci if (intf->handlers->set_run_to_completion) 51238c2ecf20Sopenharmony_ci intf->handlers->set_run_to_completion(intf->send_info, 51248c2ecf20Sopenharmony_ci 1); 51258c2ecf20Sopenharmony_ci 51268c2ecf20Sopenharmony_ci list_for_each_entry_rcu(user, &intf->users, link) { 51278c2ecf20Sopenharmony_ci if (user->handler->ipmi_panic_handler) 51288c2ecf20Sopenharmony_ci user->handler->ipmi_panic_handler( 51298c2ecf20Sopenharmony_ci user->handler_data); 51308c2ecf20Sopenharmony_ci } 51318c2ecf20Sopenharmony_ci 51328c2ecf20Sopenharmony_ci send_panic_events(intf, ptr); 51338c2ecf20Sopenharmony_ci } 51348c2ecf20Sopenharmony_ci 51358c2ecf20Sopenharmony_ci return NOTIFY_DONE; 51368c2ecf20Sopenharmony_ci} 51378c2ecf20Sopenharmony_ci 51388c2ecf20Sopenharmony_ci/* Must be called with ipmi_interfaces_mutex held. */ 51398c2ecf20Sopenharmony_cistatic int ipmi_register_driver(void) 51408c2ecf20Sopenharmony_ci{ 51418c2ecf20Sopenharmony_ci int rv; 51428c2ecf20Sopenharmony_ci 51438c2ecf20Sopenharmony_ci if (drvregistered) 51448c2ecf20Sopenharmony_ci return 0; 51458c2ecf20Sopenharmony_ci 51468c2ecf20Sopenharmony_ci rv = driver_register(&ipmidriver.driver); 51478c2ecf20Sopenharmony_ci if (rv) 51488c2ecf20Sopenharmony_ci pr_err("Could not register IPMI driver\n"); 51498c2ecf20Sopenharmony_ci else 51508c2ecf20Sopenharmony_ci drvregistered = true; 51518c2ecf20Sopenharmony_ci return rv; 51528c2ecf20Sopenharmony_ci} 51538c2ecf20Sopenharmony_ci 51548c2ecf20Sopenharmony_cistatic struct notifier_block panic_block = { 51558c2ecf20Sopenharmony_ci .notifier_call = panic_event, 51568c2ecf20Sopenharmony_ci .next = NULL, 51578c2ecf20Sopenharmony_ci .priority = 200 /* priority: INT_MAX >= x >= 0 */ 51588c2ecf20Sopenharmony_ci}; 51598c2ecf20Sopenharmony_ci 51608c2ecf20Sopenharmony_cistatic int ipmi_init_msghandler(void) 51618c2ecf20Sopenharmony_ci{ 51628c2ecf20Sopenharmony_ci int rv; 51638c2ecf20Sopenharmony_ci 51648c2ecf20Sopenharmony_ci mutex_lock(&ipmi_interfaces_mutex); 51658c2ecf20Sopenharmony_ci rv = ipmi_register_driver(); 51668c2ecf20Sopenharmony_ci if (rv) 51678c2ecf20Sopenharmony_ci goto out; 51688c2ecf20Sopenharmony_ci if (initialized) 51698c2ecf20Sopenharmony_ci goto out; 51708c2ecf20Sopenharmony_ci 51718c2ecf20Sopenharmony_ci rv = init_srcu_struct(&ipmi_interfaces_srcu); 51728c2ecf20Sopenharmony_ci if (rv) 51738c2ecf20Sopenharmony_ci goto out; 51748c2ecf20Sopenharmony_ci 51758c2ecf20Sopenharmony_ci remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq"); 51768c2ecf20Sopenharmony_ci if (!remove_work_wq) { 51778c2ecf20Sopenharmony_ci pr_err("unable to create ipmi-msghandler-remove-wq workqueue"); 51788c2ecf20Sopenharmony_ci rv = -ENOMEM; 51798c2ecf20Sopenharmony_ci goto out_wq; 51808c2ecf20Sopenharmony_ci } 51818c2ecf20Sopenharmony_ci 51828c2ecf20Sopenharmony_ci timer_setup(&ipmi_timer, ipmi_timeout, 0); 51838c2ecf20Sopenharmony_ci mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); 51848c2ecf20Sopenharmony_ci 51858c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, &panic_block); 51868c2ecf20Sopenharmony_ci 51878c2ecf20Sopenharmony_ci initialized = true; 51888c2ecf20Sopenharmony_ci 51898c2ecf20Sopenharmony_ciout_wq: 51908c2ecf20Sopenharmony_ci if (rv) 51918c2ecf20Sopenharmony_ci cleanup_srcu_struct(&ipmi_interfaces_srcu); 51928c2ecf20Sopenharmony_ciout: 51938c2ecf20Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 51948c2ecf20Sopenharmony_ci return rv; 51958c2ecf20Sopenharmony_ci} 51968c2ecf20Sopenharmony_ci 51978c2ecf20Sopenharmony_cistatic int __init ipmi_init_msghandler_mod(void) 51988c2ecf20Sopenharmony_ci{ 51998c2ecf20Sopenharmony_ci int rv; 52008c2ecf20Sopenharmony_ci 52018c2ecf20Sopenharmony_ci pr_info("version " IPMI_DRIVER_VERSION "\n"); 52028c2ecf20Sopenharmony_ci 52038c2ecf20Sopenharmony_ci mutex_lock(&ipmi_interfaces_mutex); 52048c2ecf20Sopenharmony_ci rv = ipmi_register_driver(); 52058c2ecf20Sopenharmony_ci mutex_unlock(&ipmi_interfaces_mutex); 52068c2ecf20Sopenharmony_ci 52078c2ecf20Sopenharmony_ci return rv; 52088c2ecf20Sopenharmony_ci} 52098c2ecf20Sopenharmony_ci 52108c2ecf20Sopenharmony_cistatic void __exit cleanup_ipmi(void) 52118c2ecf20Sopenharmony_ci{ 52128c2ecf20Sopenharmony_ci int count; 52138c2ecf20Sopenharmony_ci 52148c2ecf20Sopenharmony_ci if (initialized) { 52158c2ecf20Sopenharmony_ci destroy_workqueue(remove_work_wq); 52168c2ecf20Sopenharmony_ci 52178c2ecf20Sopenharmony_ci atomic_notifier_chain_unregister(&panic_notifier_list, 52188c2ecf20Sopenharmony_ci &panic_block); 52198c2ecf20Sopenharmony_ci 52208c2ecf20Sopenharmony_ci /* 52218c2ecf20Sopenharmony_ci * This can't be called if any interfaces exist, so no worry 52228c2ecf20Sopenharmony_ci * about shutting down the interfaces. 52238c2ecf20Sopenharmony_ci */ 52248c2ecf20Sopenharmony_ci 52258c2ecf20Sopenharmony_ci /* 52268c2ecf20Sopenharmony_ci * Tell the timer to stop, then wait for it to stop. This 52278c2ecf20Sopenharmony_ci * avoids problems with race conditions removing the timer 52288c2ecf20Sopenharmony_ci * here. 52298c2ecf20Sopenharmony_ci */ 52308c2ecf20Sopenharmony_ci atomic_set(&stop_operation, 1); 52318c2ecf20Sopenharmony_ci del_timer_sync(&ipmi_timer); 52328c2ecf20Sopenharmony_ci 52338c2ecf20Sopenharmony_ci initialized = false; 52348c2ecf20Sopenharmony_ci 52358c2ecf20Sopenharmony_ci /* Check for buffer leaks. */ 52368c2ecf20Sopenharmony_ci count = atomic_read(&smi_msg_inuse_count); 52378c2ecf20Sopenharmony_ci if (count != 0) 52388c2ecf20Sopenharmony_ci pr_warn("SMI message count %d at exit\n", count); 52398c2ecf20Sopenharmony_ci count = atomic_read(&recv_msg_inuse_count); 52408c2ecf20Sopenharmony_ci if (count != 0) 52418c2ecf20Sopenharmony_ci pr_warn("recv message count %d at exit\n", count); 52428c2ecf20Sopenharmony_ci 52438c2ecf20Sopenharmony_ci cleanup_srcu_struct(&ipmi_interfaces_srcu); 52448c2ecf20Sopenharmony_ci } 52458c2ecf20Sopenharmony_ci if (drvregistered) 52468c2ecf20Sopenharmony_ci driver_unregister(&ipmidriver.driver); 52478c2ecf20Sopenharmony_ci} 52488c2ecf20Sopenharmony_cimodule_exit(cleanup_ipmi); 52498c2ecf20Sopenharmony_ci 52508c2ecf20Sopenharmony_cimodule_init(ipmi_init_msghandler_mod); 52518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 52528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); 52538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI" 52548c2ecf20Sopenharmony_ci " interface."); 52558c2ecf20Sopenharmony_ciMODULE_VERSION(IPMI_DRIVER_VERSION); 52568c2ecf20Sopenharmony_ciMODULE_SOFTDEP("post: ipmi_devintf"); 5257