blob: 7ae89b4a21d5de5b884d943b0a906b2571e3e7e9 [file] [log] [blame]
Tomas Winkler19838fb2012-11-01 21:17:15 +02001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
4 * Copyright (c) 2003-2012, Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 */
16
17#include <linux/kernel.h>
18#include <linux/fs.h>
19#include <linux/errno.h>
20#include <linux/types.h>
21#include <linux/fcntl.h>
Tomas Winkler19838fb2012-11-01 21:17:15 +020022#include <linux/ioctl.h>
23#include <linux/cdev.h>
24#include <linux/list.h>
25#include <linux/delay.h>
26#include <linux/sched.h>
27#include <linux/uuid.h>
28#include <linux/jiffies.h>
29#include <linux/uaccess.h>
Tomas Winkler1f180352014-09-29 16:31:46 +030030#include <linux/slab.h>
Tomas Winkler19838fb2012-11-01 21:17:15 +020031
Tomas Winkler47a73802012-12-25 19:06:03 +020032#include <linux/mei.h>
Tomas Winkler19838fb2012-11-01 21:17:15 +020033
34#include "mei_dev.h"
Tomas Winkler0edb23f2013-01-08 23:07:12 +020035#include "hbm.h"
Tomas Winkler90e0b5f2013-01-08 23:07:14 +020036#include "client.h"
Tomas Winkler19838fb2012-11-01 21:17:15 +020037
Tomas Winkler1a1aca42013-01-08 23:07:21 +020038const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
39 0xac, 0xa8, 0x46, 0xe0,
40 0xff, 0x65, 0x81, 0x4c);
Tomas Winkler19838fb2012-11-01 21:17:15 +020041
42/**
43 * mei_amthif_reset_params - initializes mei device iamthif
44 *
45 * @dev: the device structure
46 */
47void mei_amthif_reset_params(struct mei_device *dev)
48{
49 /* reset iamthif parameters. */
Tomas Winkler19838fb2012-11-01 21:17:15 +020050 dev->iamthif_canceled = false;
Tomas Winkler19838fb2012-11-01 21:17:15 +020051 dev->iamthif_state = MEI_IAMTHIF_IDLE;
Alexander Usyskin4a704572013-09-02 13:29:47 +030052 dev->iamthif_stall_timer = 0;
Tomas Winkler22f96a02013-09-16 23:44:47 +030053 dev->iamthif_open_count = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +020054}
55
56/**
Masanari Iida393b1482013-04-05 01:05:05 +090057 * mei_amthif_host_init - mei initialization amthif client.
Tomas Winkler19838fb2012-11-01 21:17:15 +020058 *
59 * @dev: the device structure
Alexander Usyskind49ed642015-05-04 09:43:54 +030060 * @me_cl: me client
Tomas Winkler19838fb2012-11-01 21:17:15 +020061 *
Alexander Usyskince231392014-09-29 16:31:50 +030062 * Return: 0 on success, <0 on failure.
Tomas Winkler19838fb2012-11-01 21:17:15 +020063 */
Alexander Usyskind49ed642015-05-04 09:43:54 +030064int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
Tomas Winkler19838fb2012-11-01 21:17:15 +020065{
Tomas Winkler781d0d82013-01-08 23:07:22 +020066 struct mei_cl *cl = &dev->iamthif_cl;
Tomas Winklerd3208322014-08-24 12:08:55 +030067 int ret;
Tomas Winkler19838fb2012-11-01 21:17:15 +020068
Alexander Usyskine728ae22016-07-26 01:06:09 +030069 mutex_lock(&dev->device_lock);
70
71 if (mei_cl_is_connected(cl)) {
72 ret = 0;
73 goto out;
74 }
Alexander Usyskin025fb792016-02-07 23:35:43 +020075
Tomas Winkler6222f7b2013-01-08 23:07:23 +020076 dev->iamthif_state = MEI_IAMTHIF_IDLE;
77
Tomas Winkler781d0d82013-01-08 23:07:22 +020078 mei_cl_init(cl, dev);
Tomas Winkler19838fb2012-11-01 21:17:15 +020079
Alexander Usyskin7851e002016-02-07 23:35:40 +020080 ret = mei_cl_link(cl);
Tomas Winkler781d0d82013-01-08 23:07:22 +020081 if (ret < 0) {
Tomas Winkler79563db2015-01-11 00:07:16 +020082 dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
Alexander Usyskine728ae22016-07-26 01:06:09 +030083 goto out;
Tomas Winkler19838fb2012-11-01 21:17:15 +020084 }
Tomas Winkler781d0d82013-01-08 23:07:22 +020085
Alexander Usyskind49ed642015-05-04 09:43:54 +030086 ret = mei_cl_connect(cl, me_cl, NULL);
Tomas Winkler64092852014-02-17 15:13:21 +020087
Alexander Usyskine728ae22016-07-26 01:06:09 +030088out:
89 mutex_unlock(&dev->device_lock);
Tomas Winkler64092852014-02-17 15:13:21 +020090 return ret;
Tomas Winkler19838fb2012-11-01 21:17:15 +020091}
92
93/**
Tomas Winklerc54bf3a2015-02-10 10:39:39 +020094 * mei_amthif_read_start - queue message for sending read credential
95 *
96 * @cl: host client
Tomas Winkler3030dc02016-07-26 01:06:05 +030097 * @fp: file pointer of message recipient
Tomas Winklerc54bf3a2015-02-10 10:39:39 +020098 *
99 * Return: 0 on success, <0 on failure.
100 */
Tomas Winkler3030dc02016-07-26 01:06:05 +0300101static int mei_amthif_read_start(struct mei_cl *cl, const struct file *fp)
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200102{
103 struct mei_device *dev = cl->dev;
104 struct mei_cl_cb *cb;
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200105
Tomas Winkler3030dc02016-07-26 01:06:05 +0300106 cb = mei_cl_enqueue_ctrl_wr_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, fp);
Alexander Usyskin35eda922016-05-24 16:03:40 -0400107 if (!cb)
108 return -ENOMEM;
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200109
Alexander Usyskin46978ad2016-07-26 01:06:03 +0300110 cl->rx_flow_ctrl_creds++;
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200111
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200112 dev->iamthif_state = MEI_IAMTHIF_READING;
Alexander Usyskin97d549b2016-06-16 17:58:57 +0300113 cl->fp = cb->fp;
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200114
115 return 0;
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200116}
117
118/**
Alexander Usyskince231392014-09-29 16:31:50 +0300119 * mei_amthif_run_next_cmd - send next amt command from queue
Tomas Winkler19838fb2012-11-01 21:17:15 +0200120 *
121 * @dev: the device structure
Tomas Winkler86601722015-02-10 10:39:40 +0200122 *
123 * Return: 0 on success, <0 on failure.
Tomas Winkler19838fb2012-11-01 21:17:15 +0200124 */
Tomas Winkler86601722015-02-10 10:39:40 +0200125int mei_amthif_run_next_cmd(struct mei_device *dev)
Tomas Winkler19838fb2012-11-01 21:17:15 +0200126{
Tomas Winkler86601722015-02-10 10:39:40 +0200127 struct mei_cl *cl = &dev->iamthif_cl;
Alexander Usyskin05e314e2014-08-14 17:22:21 +0300128 struct mei_cl_cb *cb;
Alexander Usyskin22393382016-05-24 16:03:39 -0400129 int ret;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200130
Tomas Winkler19838fb2012-11-01 21:17:15 +0200131 dev->iamthif_canceled = false;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200132
Tomas Winkler2bf94cab2014-09-29 16:31:42 +0300133 dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
Tomas Winkler19838fb2012-11-01 21:17:15 +0200134
Alexander Usyskin140c7552014-10-02 13:39:31 +0300135 cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
136 typeof(*cb), list);
Alexander Usyskin22393382016-05-24 16:03:39 -0400137 if (!cb) {
138 dev->iamthif_state = MEI_IAMTHIF_IDLE;
Alexander Usyskin97d549b2016-06-16 17:58:57 +0300139 cl->fp = NULL;
Tomas Winkler86601722015-02-10 10:39:40 +0200140 return 0;
Alexander Usyskin22393382016-05-24 16:03:39 -0400141 }
Tomas Winkler86601722015-02-10 10:39:40 +0200142
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200143 list_del_init(&cb->list);
Alexander Usyskin22393382016-05-24 16:03:39 -0400144 dev->iamthif_state = MEI_IAMTHIF_WRITING;
Alexander Usyskin97d549b2016-06-16 17:58:57 +0300145 cl->fp = cb->fp;
Alexander Usyskin22393382016-05-24 16:03:39 -0400146
147 ret = mei_cl_write(cl, cb, false);
148 if (ret < 0)
149 return ret;
150
151 if (cb->completed)
152 cb->status = mei_amthif_read_start(cl, cb->fp);
153
154 return 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200155}
156
Tomas Winkler86601722015-02-10 10:39:40 +0200157/**
158 * mei_amthif_write - write amthif data to amthif client
159 *
160 * @cl: host client
161 * @cb: mei call back struct
162 *
163 * Return: 0 on success, <0 on failure.
164 */
165int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
166{
167
Tomas Winkler77b007b2016-02-07 23:35:27 +0200168 struct mei_device *dev = cl->dev;
Tomas Winkler86601722015-02-10 10:39:40 +0200169
Tomas Winkler86601722015-02-10 10:39:40 +0200170 list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
Alexander Usyskin5cf8b2a2016-02-07 23:35:25 +0200171
172 /*
173 * The previous request is still in processing, queue this one.
174 */
Alexander Usyskinca455fa2016-05-24 16:03:35 -0400175 if (dev->iamthif_state != MEI_IAMTHIF_IDLE)
Alexander Usyskin5cf8b2a2016-02-07 23:35:25 +0200176 return 0;
177
Tomas Winkler86601722015-02-10 10:39:40 +0200178 return mei_amthif_run_next_cmd(dev);
179}
Tomas Winkler744f0f22012-11-11 17:38:02 +0200180
Tomas Winkler1d9013f2015-03-27 00:27:57 +0200181/**
182 * mei_amthif_poll - the amthif poll function
183 *
Tomas Winkler1d9013f2015-03-27 00:27:57 +0200184 * @file: pointer to file structure
185 * @wait: pointer to poll_table structure
186 *
187 * Return: poll mask
188 *
189 * Locking: called under "dev->device_lock" lock
190 */
Alexander Usyskinca455fa2016-05-24 16:03:35 -0400191unsigned int mei_amthif_poll(struct file *file, poll_table *wait)
Tomas Winkler744f0f22012-11-11 17:38:02 +0200192{
Alexander Usyskinca455fa2016-05-24 16:03:35 -0400193 struct mei_cl *cl = file->private_data;
194 struct mei_cl_cb *cb = mei_cl_read_cb(cl, file);
Tomas Winkler744f0f22012-11-11 17:38:02 +0200195 unsigned int mask = 0;
Tomas Winklerb950ac12013-07-25 20:15:53 +0300196
Alexander Usyskinca455fa2016-05-24 16:03:35 -0400197 poll_wait(file, &cl->rx_wait, wait);
198 if (cb)
Tomas Winkler1d9013f2015-03-27 00:27:57 +0200199 mask |= POLLIN | POLLRDNORM;
Tomas Winklerb950ac12013-07-25 20:15:53 +0300200
Tomas Winkler744f0f22012-11-11 17:38:02 +0200201 return mask;
202}
203
Tomas Winkler19838fb2012-11-01 21:17:15 +0200204/**
Tomas Winkler9d098192014-02-19 17:35:48 +0200205 * mei_amthif_irq_write - write iamthif command in irq thread context.
Tomas Winkler19838fb2012-11-01 21:17:15 +0200206 *
Tomas Winkler19838fb2012-11-01 21:17:15 +0200207 * @cl: private data of the file object.
Alexander Usyskina8605ea2014-09-29 16:31:49 +0300208 * @cb: callback block.
Tomas Winkler19838fb2012-11-01 21:17:15 +0200209 * @cmpl_list: complete list.
210 *
Alexander Usyskina8605ea2014-09-29 16:31:49 +0300211 * Return: 0, OK; otherwise, error.
Tomas Winkler19838fb2012-11-01 21:17:15 +0200212 */
Tomas Winkler9d098192014-02-19 17:35:48 +0200213int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
214 struct mei_cl_cb *cmpl_list)
Tomas Winkler19838fb2012-11-01 21:17:15 +0200215{
Tomas Winkler86601722015-02-10 10:39:40 +0200216 int ret;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200217
Tomas Winkler86601722015-02-10 10:39:40 +0200218 ret = mei_cl_irq_write(cl, cb, cmpl_list);
219 if (ret)
220 return ret;
Tomas Winkler136698e2013-09-16 23:44:44 +0300221
Tomas Winkler86601722015-02-10 10:39:40 +0200222 if (cb->completed)
Tomas Winkler62e8e6a2016-02-07 23:35:24 +0200223 cb->status = mei_amthif_read_start(cl, cb->fp);
Tomas Winkler24c656e2012-11-18 15:13:17 +0200224
Tomas Winkler19838fb2012-11-01 21:17:15 +0200225 return 0;
226}
227
228/**
Alexander Usyskince231392014-09-29 16:31:50 +0300229 * mei_amthif_irq_read_msg - read routine after ISR to
Tomas Winkler1a1aca42013-01-08 23:07:21 +0200230 * handle the read amthif message
Tomas Winkler19838fb2012-11-01 21:17:15 +0200231 *
Tomas Winklerdb4756f2015-02-10 10:39:37 +0200232 * @cl: mei client
Tomas Winkler1a1aca42013-01-08 23:07:21 +0200233 * @mei_hdr: header of amthif message
Tomas Winkler331e4182015-02-10 10:39:41 +0200234 * @cmpl_list: completed callbacks list
Tomas Winkler19838fb2012-11-01 21:17:15 +0200235 *
Tomas Winkler331e4182015-02-10 10:39:41 +0200236 * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status
Tomas Winkler19838fb2012-11-01 21:17:15 +0200237 */
Tomas Winklerdb4756f2015-02-10 10:39:37 +0200238int mei_amthif_irq_read_msg(struct mei_cl *cl,
Tomas Winkler5ceb46e2013-04-19 21:16:53 +0300239 struct mei_msg_hdr *mei_hdr,
Tomas Winkler331e4182015-02-10 10:39:41 +0200240 struct mei_cl_cb *cmpl_list)
Tomas Winkler19838fb2012-11-01 21:17:15 +0200241{
Tomas Winklerdb4756f2015-02-10 10:39:37 +0200242 struct mei_device *dev;
Tomas Winkler331e4182015-02-10 10:39:41 +0200243 int ret;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200244
Tomas Winklerdb4756f2015-02-10 10:39:37 +0200245 dev = cl->dev;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200246
Alexander Usyskin9d04ee12016-04-17 12:16:04 -0400247 if (dev->iamthif_state != MEI_IAMTHIF_READING) {
248 mei_irq_discard_msg(dev, mei_hdr);
Tomas Winkler331e4182015-02-10 10:39:41 +0200249 return 0;
Alexander Usyskin9d04ee12016-04-17 12:16:04 -0400250 }
Tomas Winkler19838fb2012-11-01 21:17:15 +0200251
Tomas Winkler331e4182015-02-10 10:39:41 +0200252 ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
253 if (ret)
254 return ret;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200255
256 if (!mei_hdr->msg_complete)
257 return 0;
258
Tomas Winkler2bf94cab2014-09-29 16:31:42 +0300259 dev_dbg(dev->dev, "completed amthif read.\n ");
Tomas Winkler19838fb2012-11-01 21:17:15 +0200260 dev->iamthif_stall_timer = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200261
Tomas Winkler19838fb2012-11-01 21:17:15 +0200262 return 0;
263}
264
265/**
266 * mei_amthif_complete - complete amthif callback.
267 *
Tomas Winkler9abd8b32016-02-07 23:35:26 +0200268 * @cl: host client
Alexander Usyskina8605ea2014-09-29 16:31:49 +0300269 * @cb: callback block.
Tomas Winkler19838fb2012-11-01 21:17:15 +0200270 */
Tomas Winkler9abd8b32016-02-07 23:35:26 +0200271void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
Tomas Winkler19838fb2012-11-01 21:17:15 +0200272{
Tomas Winkler9abd8b32016-02-07 23:35:26 +0200273 struct mei_device *dev = cl->dev;
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200274
Alexander Usyskin32a1dc12016-05-24 16:03:36 -0400275 dev_dbg(dev->dev, "completing amthif call back.\n");
276 switch (cb->fop_type) {
277 case MEI_FOP_WRITE:
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200278 if (!cb->status) {
279 dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
Alexander Usyskin1892fc22016-09-25 13:25:31 +0300280 mei_schedule_stall_timer(dev);
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200281 mei_io_cb_free(cb);
282 return;
283 }
Alexander Usyskin32a1dc12016-05-24 16:03:36 -0400284 dev->iamthif_state = MEI_IAMTHIF_IDLE;
Alexander Usyskin97d549b2016-06-16 17:58:57 +0300285 cl->fp = NULL;
Alexander Usyskin32a1dc12016-05-24 16:03:36 -0400286 if (!dev->iamthif_canceled) {
287 /*
288 * in case of error enqueue the write cb to complete
289 * read list so it can be propagated to the reader
290 */
291 list_add_tail(&cb->list, &cl->rd_completed);
292 wake_up_interruptible(&cl->rx_wait);
293 } else {
294 mei_io_cb_free(cb);
295 }
296 break;
297 case MEI_FOP_READ:
298 if (!dev->iamthif_canceled) {
299 list_add_tail(&cb->list, &cl->rd_completed);
300 dev_dbg(dev->dev, "amthif read completed\n");
301 wake_up_interruptible(&cl->rx_wait);
302 } else {
303 mei_io_cb_free(cb);
304 }
305
Alexander Usyskin32a1dc12016-05-24 16:03:36 -0400306 dev->iamthif_stall_timer = 0;
307 mei_amthif_run_next_cmd(dev);
308 break;
309 default:
310 WARN_ON(1);
Tomas Winklerc54bf3a2015-02-10 10:39:39 +0200311 }
Tomas Winkler19838fb2012-11-01 21:17:15 +0200312}
313
Tomas Winklera562d5c2012-11-11 17:38:01 +0200314/**
315 * mei_clear_list - removes all callbacks associated with file
316 * from mei_cb_list
317 *
Tomas Winklera562d5c2012-11-11 17:38:01 +0200318 * @file: file structure
319 * @mei_cb_list: callbacks list
320 *
321 * mei_clear_list is called to clear resources associated with file
322 * when application calls close function or Ctrl-C was pressed
Tomas Winklera562d5c2012-11-11 17:38:01 +0200323 */
Alexander Usyskinc85dba92016-06-16 17:58:59 +0300324static void mei_clear_list(const struct file *file,
325 struct list_head *mei_cb_list)
Tomas Winklera562d5c2012-11-11 17:38:01 +0200326{
Tomas Winkler928fa662015-02-10 10:39:45 +0200327 struct mei_cl_cb *cb, *next;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200328
Alexander Usyskin32a1dc12016-05-24 16:03:36 -0400329 list_for_each_entry_safe(cb, next, mei_cb_list, list)
330 if (file == cb->fp)
Tomas Winkler928fa662015-02-10 10:39:45 +0200331 mei_io_cb_free(cb);
Tomas Winklera562d5c2012-11-11 17:38:01 +0200332}
333
334/**
Tomas Winklera562d5c2012-11-11 17:38:01 +0200335* mei_amthif_release - the release function
336*
Masanari Iida393b1482013-04-05 01:05:05 +0900337* @dev: device structure
Tomas Winklera562d5c2012-11-11 17:38:01 +0200338* @file: pointer to file structure
339*
Alexander Usyskina8605ea2014-09-29 16:31:49 +0300340* Return: 0 on success, <0 on error
Tomas Winklera562d5c2012-11-11 17:38:01 +0200341*/
342int mei_amthif_release(struct mei_device *dev, struct file *file)
343{
Alexander Usyskin97d549b2016-06-16 17:58:57 +0300344 struct mei_cl *cl = file->private_data;
345
Tomas Winkler22f96a02013-09-16 23:44:47 +0300346 if (dev->iamthif_open_count > 0)
347 dev->iamthif_open_count--;
Tomas Winklera562d5c2012-11-11 17:38:01 +0200348
Alexander Usyskin97d549b2016-06-16 17:58:57 +0300349 if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) {
Tomas Winklera562d5c2012-11-11 17:38:01 +0200350
Tomas Winkler2bf94cab2014-09-29 16:31:42 +0300351 dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
Tomas Winklera562d5c2012-11-11 17:38:01 +0200352 dev->iamthif_state);
353 dev->iamthif_canceled = true;
Tomas Winklera562d5c2012-11-11 17:38:01 +0200354 }
355
Alexander Usyskinc85dba92016-06-16 17:58:59 +0300356 mei_clear_list(file, &dev->amthif_cmd_list.list);
357 mei_clear_list(file, &cl->rd_completed);
358 mei_clear_list(file, &dev->ctrl_rd_list.list);
Tomas Winklera562d5c2012-11-11 17:38:01 +0200359
360 return 0;
361}