brintos

brintos / linux-shallow public Read only

0
0
Text · 13.3 KiB · cf670e8 Raw
569 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2015-2018, Intel Corporation.4 */5 6#define pr_fmt(fmt) "kcs-bmc: " fmt7 8#include <linux/errno.h>9#include <linux/io.h>10#include <linux/ipmi_bmc.h>11#include <linux/list.h>12#include <linux/miscdevice.h>13#include <linux/module.h>14#include <linux/mutex.h>15#include <linux/platform_device.h>16#include <linux/poll.h>17#include <linux/sched.h>18#include <linux/slab.h>19 20#include "kcs_bmc_client.h"21 22/* Different phases of the KCS BMC module.23 *  KCS_PHASE_IDLE:24 *            BMC should not be expecting nor sending any data.25 *  KCS_PHASE_WRITE_START:26 *            BMC is receiving a WRITE_START command from system software.27 *  KCS_PHASE_WRITE_DATA:28 *            BMC is receiving a data byte from system software.29 *  KCS_PHASE_WRITE_END_CMD:30 *            BMC is waiting a last data byte from system software.31 *  KCS_PHASE_WRITE_DONE:32 *            BMC has received the whole request from system software.33 *  KCS_PHASE_WAIT_READ:34 *            BMC is waiting the response from the upper IPMI service.35 *  KCS_PHASE_READ:36 *            BMC is transferring the response to system software.37 *  KCS_PHASE_ABORT_ERROR1:38 *            BMC is waiting error status request from system software.39 *  KCS_PHASE_ABORT_ERROR2:40 *            BMC is waiting for idle status afer error from system software.41 *  KCS_PHASE_ERROR:42 *            BMC has detected a protocol violation at the interface level.43 */44enum kcs_ipmi_phases {45	KCS_PHASE_IDLE,46 47	KCS_PHASE_WRITE_START,48	KCS_PHASE_WRITE_DATA,49	KCS_PHASE_WRITE_END_CMD,50	KCS_PHASE_WRITE_DONE,51 52	KCS_PHASE_WAIT_READ,53	KCS_PHASE_READ,54 55	KCS_PHASE_ABORT_ERROR1,56	KCS_PHASE_ABORT_ERROR2,57	KCS_PHASE_ERROR58};59 60/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */61enum kcs_ipmi_errors {62	KCS_NO_ERROR                = 0x00,63	KCS_ABORTED_BY_COMMAND      = 0x01,64	KCS_ILLEGAL_CONTROL_CODE    = 0x02,65	KCS_LENGTH_ERROR            = 0x06,66	KCS_UNSPECIFIED_ERROR       = 0xFF67};68 69struct kcs_bmc_ipmi {70	struct list_head entry;71 72	struct kcs_bmc_client client;73 74	spinlock_t lock;75 76	enum kcs_ipmi_phases phase;77	enum kcs_ipmi_errors error;78 79	wait_queue_head_t queue;80	bool data_in_avail;81	int  data_in_idx;82	u8  *data_in;83 84	int  data_out_idx;85	int  data_out_len;86	u8  *data_out;87 88	struct mutex mutex;89	u8 *kbuffer;90 91	struct miscdevice miscdev;92};93 94#define DEVICE_NAME "ipmi-kcs"95 96#define KCS_MSG_BUFSIZ    100097 98#define KCS_ZERO_DATA     099 100/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */101#define KCS_STATUS_STATE(state) (state << 6)102#define KCS_STATUS_STATE_MASK   GENMASK(7, 6)103#define KCS_STATUS_CMD_DAT      BIT(3)104#define KCS_STATUS_SMS_ATN      BIT(2)105#define KCS_STATUS_IBF          BIT(1)106#define KCS_STATUS_OBF          BIT(0)107 108/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */109enum kcs_states {110	IDLE_STATE  = 0,111	READ_STATE  = 1,112	WRITE_STATE = 2,113	ERROR_STATE = 3,114};115 116/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */117#define KCS_CMD_GET_STATUS_ABORT  0x60118#define KCS_CMD_WRITE_START       0x61119#define KCS_CMD_WRITE_END         0x62120#define KCS_CMD_READ_BYTE         0x68121 122static inline void set_state(struct kcs_bmc_ipmi *priv, u8 state)123{124	kcs_bmc_update_status(priv->client.dev, KCS_STATUS_STATE_MASK, KCS_STATUS_STATE(state));125}126 127static void kcs_bmc_ipmi_force_abort(struct kcs_bmc_ipmi *priv)128{129	set_state(priv, ERROR_STATE);130	kcs_bmc_read_data(priv->client.dev);131	kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA);132 133	priv->phase = KCS_PHASE_ERROR;134	priv->data_in_avail = false;135	priv->data_in_idx = 0;136}137 138static void kcs_bmc_ipmi_handle_data(struct kcs_bmc_ipmi *priv)139{140	struct kcs_bmc_device *dev;141	u8 data;142 143	dev = priv->client.dev;144 145	switch (priv->phase) {146	case KCS_PHASE_WRITE_START:147		priv->phase = KCS_PHASE_WRITE_DATA;148		fallthrough;149 150	case KCS_PHASE_WRITE_DATA:151		if (priv->data_in_idx < KCS_MSG_BUFSIZ) {152			set_state(priv, WRITE_STATE);153			kcs_bmc_write_data(dev, KCS_ZERO_DATA);154			priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev);155		} else {156			kcs_bmc_ipmi_force_abort(priv);157			priv->error = KCS_LENGTH_ERROR;158		}159		break;160 161	case KCS_PHASE_WRITE_END_CMD:162		if (priv->data_in_idx < KCS_MSG_BUFSIZ) {163			set_state(priv, READ_STATE);164			priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev);165			priv->phase = KCS_PHASE_WRITE_DONE;166			priv->data_in_avail = true;167			wake_up_interruptible(&priv->queue);168		} else {169			kcs_bmc_ipmi_force_abort(priv);170			priv->error = KCS_LENGTH_ERROR;171		}172		break;173 174	case KCS_PHASE_READ:175		if (priv->data_out_idx == priv->data_out_len)176			set_state(priv, IDLE_STATE);177 178		data = kcs_bmc_read_data(dev);179		if (data != KCS_CMD_READ_BYTE) {180			set_state(priv, ERROR_STATE);181			kcs_bmc_write_data(dev, KCS_ZERO_DATA);182			break;183		}184 185		if (priv->data_out_idx == priv->data_out_len) {186			kcs_bmc_write_data(dev, KCS_ZERO_DATA);187			priv->phase = KCS_PHASE_IDLE;188			break;189		}190 191		kcs_bmc_write_data(dev, priv->data_out[priv->data_out_idx++]);192		break;193 194	case KCS_PHASE_ABORT_ERROR1:195		set_state(priv, READ_STATE);196		kcs_bmc_read_data(dev);197		kcs_bmc_write_data(dev, priv->error);198		priv->phase = KCS_PHASE_ABORT_ERROR2;199		break;200 201	case KCS_PHASE_ABORT_ERROR2:202		set_state(priv, IDLE_STATE);203		kcs_bmc_read_data(dev);204		kcs_bmc_write_data(dev, KCS_ZERO_DATA);205		priv->phase = KCS_PHASE_IDLE;206		break;207 208	default:209		kcs_bmc_ipmi_force_abort(priv);210		break;211	}212}213 214static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc_ipmi *priv)215{216	u8 cmd;217 218	set_state(priv, WRITE_STATE);219	kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA);220 221	cmd = kcs_bmc_read_data(priv->client.dev);222	switch (cmd) {223	case KCS_CMD_WRITE_START:224		priv->phase = KCS_PHASE_WRITE_START;225		priv->error = KCS_NO_ERROR;226		priv->data_in_avail = false;227		priv->data_in_idx = 0;228		break;229 230	case KCS_CMD_WRITE_END:231		if (priv->phase != KCS_PHASE_WRITE_DATA) {232			kcs_bmc_ipmi_force_abort(priv);233			break;234		}235 236		priv->phase = KCS_PHASE_WRITE_END_CMD;237		break;238 239	case KCS_CMD_GET_STATUS_ABORT:240		if (priv->error == KCS_NO_ERROR)241			priv->error = KCS_ABORTED_BY_COMMAND;242 243		priv->phase = KCS_PHASE_ABORT_ERROR1;244		priv->data_in_avail = false;245		priv->data_in_idx = 0;246		break;247 248	default:249		kcs_bmc_ipmi_force_abort(priv);250		priv->error = KCS_ILLEGAL_CONTROL_CODE;251		break;252	}253}254 255static inline struct kcs_bmc_ipmi *client_to_kcs_bmc_ipmi(struct kcs_bmc_client *client)256{257	return container_of(client, struct kcs_bmc_ipmi, client);258}259 260static irqreturn_t kcs_bmc_ipmi_event(struct kcs_bmc_client *client)261{262	struct kcs_bmc_ipmi *priv;263	u8 status;264	int ret;265 266	priv = client_to_kcs_bmc_ipmi(client);267	if (!priv)268		return IRQ_NONE;269 270	spin_lock(&priv->lock);271 272	status = kcs_bmc_read_status(client->dev);273	if (status & KCS_STATUS_IBF) {274		if (status & KCS_STATUS_CMD_DAT)275			kcs_bmc_ipmi_handle_cmd(priv);276		else277			kcs_bmc_ipmi_handle_data(priv);278 279		ret = IRQ_HANDLED;280	} else {281		ret = IRQ_NONE;282	}283 284	spin_unlock(&priv->lock);285 286	return ret;287}288 289static const struct kcs_bmc_client_ops kcs_bmc_ipmi_client_ops = {290	.event = kcs_bmc_ipmi_event,291};292 293static inline struct kcs_bmc_ipmi *to_kcs_bmc(struct file *filp)294{295	return container_of(filp->private_data, struct kcs_bmc_ipmi, miscdev);296}297 298static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)299{300	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);301 302	return kcs_bmc_enable_device(priv->client.dev, &priv->client);303}304 305static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)306{307	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);308	__poll_t mask = 0;309 310	poll_wait(filp, &priv->queue, wait);311 312	spin_lock_irq(&priv->lock);313	if (priv->data_in_avail)314		mask |= EPOLLIN;315	spin_unlock_irq(&priv->lock);316 317	return mask;318}319 320static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,321			    size_t count, loff_t *ppos)322{323	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);324	bool data_avail;325	size_t data_len;326	ssize_t ret;327 328	if (!(filp->f_flags & O_NONBLOCK))329		wait_event_interruptible(priv->queue,330					 priv->data_in_avail);331 332	mutex_lock(&priv->mutex);333 334	spin_lock_irq(&priv->lock);335	data_avail = priv->data_in_avail;336	if (data_avail) {337		data_len = priv->data_in_idx;338		memcpy(priv->kbuffer, priv->data_in, data_len);339	}340	spin_unlock_irq(&priv->lock);341 342	if (!data_avail) {343		ret = -EAGAIN;344		goto out_unlock;345	}346 347	if (count < data_len) {348		pr_err("channel=%u with too large data : %zu\n",349			priv->client.dev->channel, data_len);350 351		spin_lock_irq(&priv->lock);352		kcs_bmc_ipmi_force_abort(priv);353		spin_unlock_irq(&priv->lock);354 355		ret = -EOVERFLOW;356		goto out_unlock;357	}358 359	if (copy_to_user(buf, priv->kbuffer, data_len)) {360		ret = -EFAULT;361		goto out_unlock;362	}363 364	ret = data_len;365 366	spin_lock_irq(&priv->lock);367	if (priv->phase == KCS_PHASE_WRITE_DONE) {368		priv->phase = KCS_PHASE_WAIT_READ;369		priv->data_in_avail = false;370		priv->data_in_idx = 0;371	} else {372		ret = -EAGAIN;373	}374	spin_unlock_irq(&priv->lock);375 376out_unlock:377	mutex_unlock(&priv->mutex);378 379	return ret;380}381 382static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,383			     size_t count, loff_t *ppos)384{385	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);386	ssize_t ret;387 388	/* a minimum response size '3' : netfn + cmd + ccode */389	if (count < 3 || count > KCS_MSG_BUFSIZ)390		return -EINVAL;391 392	mutex_lock(&priv->mutex);393 394	if (copy_from_user(priv->kbuffer, buf, count)) {395		ret = -EFAULT;396		goto out_unlock;397	}398 399	spin_lock_irq(&priv->lock);400	if (priv->phase == KCS_PHASE_WAIT_READ) {401		priv->phase = KCS_PHASE_READ;402		priv->data_out_idx = 1;403		priv->data_out_len = count;404		memcpy(priv->data_out, priv->kbuffer, count);405		kcs_bmc_write_data(priv->client.dev, priv->data_out[0]);406		ret = count;407	} else {408		ret = -EINVAL;409	}410	spin_unlock_irq(&priv->lock);411 412out_unlock:413	mutex_unlock(&priv->mutex);414 415	return ret;416}417 418static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,419			  unsigned long arg)420{421	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);422	long ret = 0;423 424	spin_lock_irq(&priv->lock);425 426	switch (cmd) {427	case IPMI_BMC_IOCTL_SET_SMS_ATN:428		kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);429		break;430 431	case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:432		kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, 0);433		break;434 435	case IPMI_BMC_IOCTL_FORCE_ABORT:436		kcs_bmc_ipmi_force_abort(priv);437		break;438 439	default:440		ret = -EINVAL;441		break;442	}443 444	spin_unlock_irq(&priv->lock);445 446	return ret;447}448 449static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)450{451	struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp);452 453	kcs_bmc_ipmi_force_abort(priv);454	kcs_bmc_disable_device(priv->client.dev, &priv->client);455 456	return 0;457}458 459static const struct file_operations kcs_bmc_ipmi_fops = {460	.owner          = THIS_MODULE,461	.open           = kcs_bmc_ipmi_open,462	.read           = kcs_bmc_ipmi_read,463	.write          = kcs_bmc_ipmi_write,464	.release        = kcs_bmc_ipmi_release,465	.poll           = kcs_bmc_ipmi_poll,466	.unlocked_ioctl = kcs_bmc_ipmi_ioctl,467};468 469static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock);470static LIST_HEAD(kcs_bmc_ipmi_instances);471 472static int kcs_bmc_ipmi_add_device(struct kcs_bmc_device *kcs_bmc)473{474	struct kcs_bmc_ipmi *priv;475	int rc;476 477	priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);478	if (!priv)479		return -ENOMEM;480 481	spin_lock_init(&priv->lock);482	mutex_init(&priv->mutex);483 484	init_waitqueue_head(&priv->queue);485 486	priv->client.dev = kcs_bmc;487	priv->client.ops = &kcs_bmc_ipmi_client_ops;488	priv->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);489	priv->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);490	priv->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);491 492	priv->miscdev.minor = MISC_DYNAMIC_MINOR;493	priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME,494					   kcs_bmc->channel);495	if (!priv->data_in || !priv->data_out || !priv->kbuffer || !priv->miscdev.name)496		return -EINVAL;497 498	priv->miscdev.fops = &kcs_bmc_ipmi_fops;499 500	rc = misc_register(&priv->miscdev);501	if (rc) {502		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);503		return rc;504	}505 506	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);507	list_add(&priv->entry, &kcs_bmc_ipmi_instances);508	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);509 510	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);511 512	return 0;513}514 515static int kcs_bmc_ipmi_remove_device(struct kcs_bmc_device *kcs_bmc)516{517	struct kcs_bmc_ipmi *priv = NULL, *pos;518 519	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);520	list_for_each_entry(pos, &kcs_bmc_ipmi_instances, entry) {521		if (pos->client.dev == kcs_bmc) {522			priv = pos;523			list_del(&pos->entry);524			break;525		}526	}527	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);528 529	if (!priv)530		return -ENODEV;531 532	misc_deregister(&priv->miscdev);533	kcs_bmc_disable_device(priv->client.dev, &priv->client);534	devm_kfree(kcs_bmc->dev, priv->kbuffer);535	devm_kfree(kcs_bmc->dev, priv->data_out);536	devm_kfree(kcs_bmc->dev, priv->data_in);537	devm_kfree(kcs_bmc->dev, priv);538 539	return 0;540}541 542static const struct kcs_bmc_driver_ops kcs_bmc_ipmi_driver_ops = {543	.add_device = kcs_bmc_ipmi_add_device,544	.remove_device = kcs_bmc_ipmi_remove_device,545};546 547static struct kcs_bmc_driver kcs_bmc_ipmi_driver = {548	.ops = &kcs_bmc_ipmi_driver_ops,549};550 551static int __init kcs_bmc_ipmi_init(void)552{553	kcs_bmc_register_driver(&kcs_bmc_ipmi_driver);554 555	return 0;556}557module_init(kcs_bmc_ipmi_init);558 559static void __exit kcs_bmc_ipmi_exit(void)560{561	kcs_bmc_unregister_driver(&kcs_bmc_ipmi_driver);562}563module_exit(kcs_bmc_ipmi_exit);564 565MODULE_LICENSE("GPL v2");566MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");567MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");568MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");569