amthif.c 15.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 *
 * Intel Management Engine Interface (Intel MEI) Linux driver
 * Copyright (c) 2003-2012, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 */

#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/aio.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/uuid.h>
#include <linux/jiffies.h>
#include <linux/uaccess.h>
31
#include <linux/slab.h>
32

33
#include <linux/mei.h>
34 35

#include "mei_dev.h"
36
#include "hbm.h"
T
Tomas Winkler 已提交
37
#include "client.h"
38

39 40 41
const uuid_le mei_amthif_guid  = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
					 0xac, 0xa8, 0x46, 0xe0,
					 0xff, 0x65, 0x81, 0x4c);
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

/**
 * mei_amthif_reset_params - initializes mei device iamthif
 *
 * @dev: the device structure
 */
void mei_amthif_reset_params(struct mei_device *dev)
{
	/* reset iamthif parameters. */
	dev->iamthif_current_cb = NULL;
	dev->iamthif_msg_buf_size = 0;
	dev->iamthif_msg_buf_index = 0;
	dev->iamthif_canceled = false;
	dev->iamthif_state = MEI_IAMTHIF_IDLE;
	dev->iamthif_timer = 0;
57
	dev->iamthif_stall_timer = 0;
T
Tomas Winkler 已提交
58
	dev->iamthif_open_count = 0;
59 60 61
}

/**
62
 * mei_amthif_host_init - mei initialization amthif client.
63 64 65
 *
 * @dev: the device structure
 *
A
Alexander Usyskin 已提交
66
 * Return: 0 on success, <0 on failure.
67
 */
68
int mei_amthif_host_init(struct mei_device *dev)
69
{
70
	struct mei_cl *cl = &dev->iamthif_cl;
71
	struct mei_me_client *me_cl;
72
	unsigned char *msg_buf;
73
	int ret;
74

75 76
	dev->iamthif_state = MEI_IAMTHIF_IDLE;

77
	mei_cl_init(cl, dev);
78

79 80
	me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
	if (!me_cl) {
81
		dev_info(dev->dev, "amthif: failed to find the client");
82
		return -ENOTTY;
83 84
	}

85
	cl->me_client_id = me_cl->client_id;
86
	cl->cl_uuid = me_cl->props.protocol_name;
87

88 89
	/* Assign iamthif_mtu to the value received from ME  */

90
	dev->iamthif_mtu = me_cl->props.max_msg_length;
91
	dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu);
92 93 94 95 96 97 98

	kfree(dev->iamthif_msg_buf);
	dev->iamthif_msg_buf = NULL;

	/* allocate storage for ME message buffer */
	msg_buf = kcalloc(dev->iamthif_mtu,
			sizeof(unsigned char), GFP_KERNEL);
99 100 101 102
	if (!msg_buf) {
		ret = -ENOMEM;
		goto out;
	}
103 104 105

	dev->iamthif_msg_buf = msg_buf;

106 107
	ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
	if (ret < 0) {
108 109
		dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
		goto out;
110 111
	}

112 113 114 115
	ret = mei_cl_connect(cl, NULL);

	dev->iamthif_state = MEI_IAMTHIF_IDLE;

116 117
out:
	mei_me_cl_put(me_cl);
118
	return ret;
119 120 121 122 123 124 125 126
}

/**
 * mei_amthif_find_read_list_entry - finds a amthilist entry for current file
 *
 * @dev: the device structure
 * @file: pointer to file object
 *
127
 * Return:   returned a list entry on success, NULL on failure.
128 129 130 131
 */
struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
						struct file *file)
{
132
	struct mei_cl_cb *cb;
133

134 135
	list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list)
		if (cb->file_object == file)
136
			return cb;
137 138 139 140 141 142 143 144 145
	return NULL;
}


/**
 * mei_amthif_read - read data from AMTHIF client
 *
 * @dev: the device structure
 * @file: pointer to file object
146
 * @ubuf: pointer to user data in user space
147 148 149 150 151
 * @length: data length to read
 * @offset: data read offset
 *
 * Locking: called under "dev->device_lock" lock
 *
152
 * Return:
153 154 155 156 157 158 159 160
 *  returned data length on success,
 *  zero if no data to read,
 *  negative on failure.
 */
int mei_amthif_read(struct mei_device *dev, struct file *file,
	       char __user *ubuf, size_t length, loff_t *offset)
{
	struct mei_cl *cl = file->private_data;
161
	struct mei_cl_cb *cb;
162
	unsigned long timeout;
163 164
	int rets;
	int wait_ret;
165

166
	/* Only possible if we are in timeout */
167
	if (!cl) {
168
		dev_err(dev->dev, "bad file ext.\n");
169
		return -ETIME;
170 171
	}

172
	dev_dbg(dev->dev, "checking amthif data\n");
173 174 175 176 177 178 179
	cb = mei_amthif_find_read_list_entry(dev, file);

	/* Check for if we can block or not*/
	if (cb == NULL && file->f_flags & O_NONBLOCK)
		return -EAGAIN;


180
	dev_dbg(dev->dev, "waiting for amthif data\n");
181 182 183 184 185 186 187
	while (cb == NULL) {
		/* unlock the Mutex */
		mutex_unlock(&dev->device_lock);

		wait_ret = wait_event_interruptible(dev->iamthif_cl.wait,
			(cb = mei_amthif_find_read_list_entry(dev, file)));

188 189 190
		/* Locking again the Mutex */
		mutex_lock(&dev->device_lock);

191 192 193
		if (wait_ret)
			return -ERESTARTSYS;

194
		dev_dbg(dev->dev, "woke up from sleep\n");
195 196
	}

197 198 199 200 201
	if (cb->status) {
		rets = cb->status;
		dev_dbg(dev->dev, "read operation failed %d\n", rets);
		goto free;
	}
202

203
	dev_dbg(dev->dev, "Got amthif data\n");
204 205
	dev->iamthif_timer = 0;

206 207 208 209 210 211 212 213 214 215 216
	timeout = cb->read_time +
		mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
	dev_dbg(dev->dev, "amthif timeout = %lud\n",
			timeout);

	if  (time_after(jiffies, timeout)) {
		dev_dbg(dev->dev, "amthif Time out\n");
		/* 15 sec for the message has expired */
		list_del(&cb->list);
		rets = -ETIME;
		goto free;
217 218 219 220 221 222 223 224 225 226 227 228 229 230
	}
	/* if the whole message will fit remove it from the list */
	if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
		list_del(&cb->list);
	else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
		/* end of the message has been reached */
		list_del(&cb->list);
		rets = 0;
		goto free;
	}
		/* else means that not full buffer will be read and do not
		 * remove message from deletion list
		 */

231
	dev_dbg(dev->dev, "amthif cb->response_buffer size - %d\n",
232
	    cb->response_buffer.size);
233
	dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx);
234

235
	/* length is being truncated to PAGE_SIZE, however,
236 237 238
	 * the buf_idx may point beyond */
	length = min_t(size_t, length, (cb->buf_idx - *offset));

239
	if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
240
		dev_dbg(dev->dev, "failed to copy data to userland\n");
241
		rets = -EFAULT;
242
	} else {
243 244 245 246 247 248 249
		rets = length;
		if ((*offset + length) < cb->buf_idx) {
			*offset += length;
			goto out;
		}
	}
free:
250
	dev_dbg(dev->dev, "free amthif cb memory.\n");
251 252 253 254 255 256
	*offset = 0;
	mei_io_cb_free(cb);
out:
	return rets;
}

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
/**
 * mei_amthif_read_start - queue message for sending read credential
 *
 * @cl: host client
 * @file: file pointer of message recipient
 *
 * Return: 0 on success, <0 on failure.
 */
static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
{
	struct mei_device *dev = cl->dev;
	struct mei_cl_cb *cb;
	size_t length = dev->iamthif_mtu;
	int rets;

	cb = mei_io_cb_init(cl, file);
	if (!cb) {
		rets = -ENOMEM;
		goto err;
	}

	rets = mei_io_cb_alloc_resp_buf(cb, length);
	if (rets)
		goto err;

	cb->fop_type = MEI_FOP_READ;
	list_add_tail(&cb->list, &dev->ctrl_wr_list.list);

	dev->iamthif_msg_buf_index = 0;
	dev->iamthif_msg_buf_size = 0;

	dev->iamthif_state = MEI_IAMTHIF_READING;
	dev->iamthif_file_object = cb->file_object;
	dev->iamthif_current_cb = cb;

	return 0;
err:
	mei_io_cb_free(cb);
	return rets;
}

298
/**
299
 * mei_amthif_send_cmd - send amthif command to the ME
300
 *
301
 * @cl: the host client
302 303
 * @cb: mei call back struct
 *
304
 * Return: 0 on success, <0 on failure.
305
 */
306
static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
307
{
308
	struct mei_device *dev;
309 310
	int ret;

311
	if (!cl->dev || !cb)
312 313
		return -ENODEV;

314
	dev = cl->dev;
315 316 317 318 319 320

	dev->iamthif_state = MEI_IAMTHIF_WRITING;
	dev->iamthif_current_cb = cb;
	dev->iamthif_file_object = cb->file_object;
	dev->iamthif_canceled = false;

321
	ret = mei_cl_write(cl, cb, false);
322 323 324
	if (ret < 0)
		return ret;

325 326
	if (cb->completed)
		cb->status = mei_amthif_read_start(cl, cb->file_object);
327

328 329 330
	return 0;
}

331
/**
332
 * mei_amthif_run_next_cmd - send next amt command from queue
333 334 335
 *
 * @dev: the device structure
 *
336
 * Return: 0 on success, <0 on failure.
337
 */
338
int mei_amthif_run_next_cmd(struct mei_device *dev)
339
{
340
	struct mei_cl *cl = &dev->iamthif_cl;
341
	struct mei_cl_cb *cb;
342 343 344 345 346 347 348 349

	dev->iamthif_msg_buf_size = 0;
	dev->iamthif_msg_buf_index = 0;
	dev->iamthif_canceled = false;
	dev->iamthif_state = MEI_IAMTHIF_IDLE;
	dev->iamthif_timer = 0;
	dev->iamthif_file_object = NULL;

350
	dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
351

352 353 354
	cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
					typeof(*cb), list);
	if (!cb)
355 356
		return 0;

357
	list_del_init(&cb->list);
358
	return mei_amthif_send_cmd(cl, cb);
359 360
}

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
/**
 * mei_amthif_write - write amthif data to amthif client
 *
 * @cl: host client
 * @cb: mei call back struct
 *
 * Return: 0 on success, <0 on failure.
 */
int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
{

	struct mei_device *dev;

	if (WARN_ON(!cl || !cl->dev))
		return -ENODEV;

	if (WARN_ON(!cb))
		return -EINVAL;

	dev = cl->dev;

	cb->fop_type = MEI_FOP_WRITE;
	list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
	return mei_amthif_run_next_cmd(dev);
}
386 387 388 389 390

unsigned int mei_amthif_poll(struct mei_device *dev,
		struct file *file, poll_table *wait)
{
	unsigned int mask = 0;
391

392
	poll_wait(file, &dev->iamthif_cl.wait, wait);
393

394
	mutex_lock(&dev->device_lock);
395 396 397 398 399 400 401
	if (!mei_cl_is_connected(&dev->iamthif_cl)) {

		mask = POLLERR;

	} else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
		   dev->iamthif_file_object == file) {

402
		mask |= (POLLIN | POLLRDNORM);
403
		dev_dbg(dev->dev, "run next amthif cb\n");
404 405
		mei_amthif_run_next_cmd(dev);
	}
406 407
	mutex_unlock(&dev->device_lock);

408 409 410 411 412
	return mask;
}



413
/**
414
 * mei_amthif_irq_write - write iamthif command in irq thread context.
415 416
 *
 * @cl: private data of the file object.
417
 * @cb: callback block.
418 419
 * @cmpl_list: complete list.
 *
420
 * Return: 0, OK; otherwise, error.
421
 */
422 423
int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
			 struct mei_cl_cb *cmpl_list)
424
{
425
	int ret;
426

427 428 429
	ret = mei_cl_irq_write(cl, cb, cmpl_list);
	if (ret)
		return ret;
430

431
	if (cb->completed)
432
		cb->status = mei_amthif_read_start(cl, cb->file_object);
433

434 435 436 437
	return 0;
}

/**
A
Alexander Usyskin 已提交
438
 * mei_amthif_irq_read_msg - read routine after ISR to
439
 *			handle the read amthif message
440
 *
441
 * @cl: mei client
442
 * @mei_hdr: header of amthif message
443
 * @complete_list: completed callbacks list
444
 *
445
 * Return: Always 0; error message is in cb->status
446
 */
447
int mei_amthif_irq_read_msg(struct mei_cl *cl,
448 449
			    struct mei_msg_hdr *mei_hdr,
			    struct mei_cl_cb *complete_list)
450
{
451
	struct mei_device *dev;
452 453 454
	struct mei_cl_cb *cb;
	unsigned char *buffer;

455
	dev = cl->dev;
456

457 458 459 460 461 462
	if (cl->state != MEI_FILE_CONNECTED)
		goto err;

	if (dev->iamthif_state != MEI_IAMTHIF_READING)
		goto err;

463 464 465 466
	list_for_each_entry(cb, &dev->read_list.list, list) {
		if (cl == cb->cl)
			break;
	}
467

468 469
	if (&cb->list == &dev->read_list.list) {
		dev_err(dev->dev, "no reader found\n");
470 471 472 473 474 475 476 477 478
		goto err;
	}

	if (dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length) {
		cb->status = -ERANGE;
		goto err;
	}

	buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index;
479 480 481 482 483 484 485
	mei_read_slots(dev, buffer, mei_hdr->length);

	dev->iamthif_msg_buf_index += mei_hdr->length;

	if (!mei_hdr->msg_complete)
		return 0;

486
	dev_dbg(dev->dev, "completed amthif read.\n ");
487 488 489 490 491 492

	dev->iamthif_current_cb = NULL;

	dev->iamthif_stall_timer = 0;
	cb->buf_idx = dev->iamthif_msg_buf_index;
	cb->read_time = jiffies;
493 494

	dev_dbg(dev->dev, "complete the amthif read cb.\n ");
495
	list_move_tail(&cb->list, &complete_list->list);
496

497
	return 0;
498 499 500 501 502

err:
	mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
	dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n",
				MEI_HDR_PRM(mei_hdr));
503 504 505 506 507 508 509
	return 0;
}

/**
 * mei_amthif_complete - complete amthif callback.
 *
 * @dev: the device structure.
510
 * @cb: callback block.
511 512 513
 */
void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
{
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529

	if (cb->fop_type == MEI_FOP_WRITE) {
		if (!cb->status) {
			dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
			mei_io_cb_free(cb);
			return;
		}
		/*
		 * in case of error enqueue the write cb to complete read list
		 * so it can be propagated to the reader
		 */
		list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
		wake_up_interruptible(&dev->iamthif_cl.wait);
		return;
	}

530 531 532 533
	if (dev->iamthif_canceled != 1) {
		dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
		dev->iamthif_stall_timer = 0;
		memcpy(cb->response_buffer.data,
534
			dev->iamthif_msg_buf, dev->iamthif_msg_buf_index);
535
		list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
536
		dev_dbg(dev->dev, "amthif read completed\n");
537
		dev->iamthif_timer = jiffies;
538
		dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n",
539
			dev->iamthif_timer);
540 541 542 543
	} else {
		mei_amthif_run_next_cmd(dev);
	}

544
	dev_dbg(dev->dev, "completing amthif call back.\n");
545 546 547
	wake_up_interruptible(&dev->iamthif_cl.wait);
}

548 549 550 551 552 553 554 555 556 557 558
/**
 * mei_clear_list - removes all callbacks associated with file
 *		from mei_cb_list
 *
 * @dev: device structure.
 * @file: file structure
 * @mei_cb_list: callbacks list
 *
 * mei_clear_list is called to clear resources associated with file
 * when application calls close function or Ctrl-C was pressed
 *
559
 * Return: true if callback removed from the list, false otherwise
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
 */
static bool mei_clear_list(struct mei_device *dev,
		const struct file *file, struct list_head *mei_cb_list)
{
	struct mei_cl_cb *cb_pos = NULL;
	struct mei_cl_cb *cb_next = NULL;
	bool removed = false;

	/* list all list member */
	list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, list) {
		/* check if list member associated with a file */
		if (file == cb_pos->file_object) {
			/* remove member from the list */
			list_del(&cb_pos->list);
			/* check if cb equal to current iamthif cb */
			if (dev->iamthif_current_cb == cb_pos) {
				dev->iamthif_current_cb = NULL;
				/* send flow control to iamthif client */
578 579
				mei_hbm_cl_flow_control_req(dev,
							&dev->iamthif_cl);
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
			}
			/* free all allocated buffers */
			mei_io_cb_free(cb_pos);
			cb_pos = NULL;
			removed = true;
		}
	}
	return removed;
}

/**
 * mei_clear_lists - removes all callbacks associated with file
 *
 * @dev: device structure
 * @file: file structure
 *
 * mei_clear_lists is called to clear resources associated with file
 * when application calls close function or Ctrl-C was pressed
 *
599
 * Return: true if callback removed from the list, false otherwise
600 601 602 603 604 605 606 607 608
 */
static bool mei_clear_lists(struct mei_device *dev, struct file *file)
{
	bool removed = false;

	/* remove callbacks associated with a file */
	mei_clear_list(dev, file, &dev->amthif_cmd_list.list);
	if (mei_clear_list(dev, file, &dev->amthif_rd_complete_list.list))
		removed = true;
609

610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
	mei_clear_list(dev, file, &dev->ctrl_rd_list.list);

	if (mei_clear_list(dev, file, &dev->ctrl_wr_list.list))
		removed = true;

	if (mei_clear_list(dev, file, &dev->write_waiting_list.list))
		removed = true;

	if (mei_clear_list(dev, file, &dev->write_list.list))
		removed = true;

	/* check if iamthif_current_cb not NULL */
	if (dev->iamthif_current_cb && !removed) {
		/* check file and iamthif current cb association */
		if (dev->iamthif_current_cb->file_object == file) {
			/* remove cb */
			mei_io_cb_free(dev->iamthif_current_cb);
			dev->iamthif_current_cb = NULL;
			removed = true;
		}
	}
	return removed;
}

/**
* mei_amthif_release - the release function
*
637
*  @dev: device structure
638 639
*  @file: pointer to file structure
*
640
*  Return: 0 on success, <0 on error
641 642 643
*/
int mei_amthif_release(struct mei_device *dev, struct file *file)
{
T
Tomas Winkler 已提交
644 645
	if (dev->iamthif_open_count > 0)
		dev->iamthif_open_count--;
646 647 648 649

	if (dev->iamthif_file_object == file &&
	    dev->iamthif_state != MEI_IAMTHIF_IDLE) {

650
		dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
651 652 653
		    dev->iamthif_state);
		dev->iamthif_canceled = true;
		if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) {
654
			dev_dbg(dev->dev, "run next amthif iamthif cb\n");
655 656 657 658 659 660 661 662 663
			mei_amthif_run_next_cmd(dev);
		}
	}

	if (mei_clear_lists(dev, file))
		dev->iamthif_state = MEI_IAMTHIF_IDLE;

	return 0;
}