hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
kernel/drivers/misc/mei/interrupt.c
....@@ -1,19 +1,8 @@
1
+// SPDX-License-Identifier: GPL-2.0
12 /*
2
- *
3
+ * Copyright (c) 2003-2018, Intel Corporation. All rights reserved.
34 * 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
- *
155 */
16
-
176
187 #include <linux/export.h>
198 #include <linux/kthread.h>
....@@ -72,14 +61,21 @@
7261 *
7362 * @dev: mei device
7463 * @hdr: message header
64
+ * @discard_len: the length of the message to discard (excluding header)
7565 */
76
-static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
66
+static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr,
67
+ size_t discard_len)
7768 {
69
+ if (hdr->dma_ring) {
70
+ mei_dma_ring_read(dev, NULL,
71
+ hdr->extension[dev->rd_msg_hdr_count - 2]);
72
+ discard_len = 0;
73
+ }
7874 /*
7975 * no need to check for size as it is guarantied
8076 * that length fits into rd_msg_buf
8177 */
82
- mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
78
+ mei_read_slots(dev, dev->rd_msg_buf, discard_len);
8379 dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n",
8480 MEI_HDR_PRM(hdr));
8581 }
....@@ -89,17 +85,29 @@
8985 *
9086 * @cl: reading client
9187 * @mei_hdr: header of mei client message
88
+ * @meta: extend meta header
9289 * @cmpl_list: completion list
9390 *
9491 * Return: always 0
9592 */
9693 static int mei_cl_irq_read_msg(struct mei_cl *cl,
9794 struct mei_msg_hdr *mei_hdr,
95
+ struct mei_ext_meta_hdr *meta,
9896 struct list_head *cmpl_list)
9997 {
10098 struct mei_device *dev = cl->dev;
10199 struct mei_cl_cb *cb;
100
+
102101 size_t buf_sz;
102
+ u32 length;
103
+ int ext_len;
104
+
105
+ length = mei_hdr->length;
106
+ ext_len = 0;
107
+ if (mei_hdr->extended) {
108
+ ext_len = sizeof(*meta) + mei_slots2data(meta->size);
109
+ length -= ext_len;
110
+ }
103111
104112 cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
105113 if (!cb) {
....@@ -113,31 +121,76 @@
113121 list_add_tail(&cb->list, &cl->rd_pending);
114122 }
115123
124
+ if (mei_hdr->extended) {
125
+ struct mei_ext_hdr *ext;
126
+ struct mei_ext_hdr *vtag = NULL;
127
+
128
+ ext = mei_ext_begin(meta);
129
+ do {
130
+ switch (ext->type) {
131
+ case MEI_EXT_HDR_VTAG:
132
+ vtag = ext;
133
+ break;
134
+ case MEI_EXT_HDR_NONE:
135
+ fallthrough;
136
+ default:
137
+ cb->status = -EPROTO;
138
+ break;
139
+ }
140
+
141
+ ext = mei_ext_next(ext);
142
+ } while (!mei_ext_last(meta, ext));
143
+
144
+ if (!vtag) {
145
+ cl_dbg(dev, cl, "vtag not found in extended header.\n");
146
+ cb->status = -EPROTO;
147
+ goto discard;
148
+ }
149
+
150
+ cl_dbg(dev, cl, "vtag: %d\n", vtag->ext_payload[0]);
151
+ if (cb->vtag && cb->vtag != vtag->ext_payload[0]) {
152
+ cl_err(dev, cl, "mismatched tag: %d != %d\n",
153
+ cb->vtag, vtag->ext_payload[0]);
154
+ cb->status = -EPROTO;
155
+ goto discard;
156
+ }
157
+ cb->vtag = vtag->ext_payload[0];
158
+ }
159
+
116160 if (!mei_cl_is_connected(cl)) {
117161 cl_dbg(dev, cl, "not connected\n");
118162 cb->status = -ENODEV;
119163 goto discard;
120164 }
121165
122
- buf_sz = mei_hdr->length + cb->buf_idx;
166
+ if (mei_hdr->dma_ring)
167
+ length = mei_hdr->extension[mei_data2slots(ext_len)];
168
+
169
+ buf_sz = length + cb->buf_idx;
123170 /* catch for integer overflow */
124171 if (buf_sz < cb->buf_idx) {
125172 cl_err(dev, cl, "message is too big len %d idx %zu\n",
126
- mei_hdr->length, cb->buf_idx);
173
+ length, cb->buf_idx);
127174 cb->status = -EMSGSIZE;
128175 goto discard;
129176 }
130177
131178 if (cb->buf.size < buf_sz) {
132179 cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n",
133
- cb->buf.size, mei_hdr->length, cb->buf_idx);
180
+ cb->buf.size, length, cb->buf_idx);
134181 cb->status = -EMSGSIZE;
135182 goto discard;
136183 }
137184
138
- mei_read_slots(dev, cb->buf.data + cb->buf_idx, mei_hdr->length);
185
+ if (mei_hdr->dma_ring) {
186
+ mei_dma_ring_read(dev, cb->buf.data + cb->buf_idx, length);
187
+ /* for DMA read 0 length to generate interrupt to the device */
188
+ mei_read_slots(dev, cb->buf.data + cb->buf_idx, 0);
189
+ } else {
190
+ mei_read_slots(dev, cb->buf.data + cb->buf_idx, length);
191
+ }
139192
140
- cb->buf_idx += mei_hdr->length;
193
+ cb->buf_idx += length;
141194
142195 if (mei_hdr->msg_complete) {
143196 cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx);
....@@ -152,7 +205,7 @@
152205 discard:
153206 if (cb)
154207 list_move_tail(&cb->list, cmpl_list);
155
- mei_irq_discard_msg(dev, mei_hdr);
208
+ mei_irq_discard_msg(dev, mei_hdr, length);
156209 return 0;
157210 }
158211
....@@ -245,9 +298,17 @@
245298 static inline int hdr_is_valid(u32 msg_hdr)
246299 {
247300 struct mei_msg_hdr *mei_hdr;
301
+ u32 expected_len = 0;
248302
249303 mei_hdr = (struct mei_msg_hdr *)&msg_hdr;
250304 if (!msg_hdr || mei_hdr->reserved)
305
+ return -EBADMSG;
306
+
307
+ if (mei_hdr->dma_ring)
308
+ expected_len += MEI_SLOT_SIZE;
309
+ if (mei_hdr->extended)
310
+ expected_len += MEI_SLOT_SIZE;
311
+ if (mei_hdr->length < expected_len)
251312 return -EBADMSG;
252313
253314 return 0;
....@@ -267,23 +328,30 @@
267328 struct list_head *cmpl_list, s32 *slots)
268329 {
269330 struct mei_msg_hdr *mei_hdr;
331
+ struct mei_ext_meta_hdr *meta_hdr = NULL;
270332 struct mei_cl *cl;
271333 int ret;
334
+ u32 ext_meta_hdr_u32;
335
+ u32 hdr_size_left;
336
+ u32 hdr_size_ext;
337
+ int i;
338
+ int ext_hdr_end;
272339
273
- if (!dev->rd_msg_hdr) {
274
- dev->rd_msg_hdr = mei_read_hdr(dev);
340
+ if (!dev->rd_msg_hdr[0]) {
341
+ dev->rd_msg_hdr[0] = mei_read_hdr(dev);
342
+ dev->rd_msg_hdr_count = 1;
275343 (*slots)--;
276344 dev_dbg(dev->dev, "slots =%08x.\n", *slots);
277345
278
- ret = hdr_is_valid(dev->rd_msg_hdr);
346
+ ret = hdr_is_valid(dev->rd_msg_hdr[0]);
279347 if (ret) {
280348 dev_err(dev->dev, "corrupted message header 0x%08X\n",
281
- dev->rd_msg_hdr);
349
+ dev->rd_msg_hdr[0]);
282350 goto end;
283351 }
284352 }
285353
286
- mei_hdr = (struct mei_msg_hdr *)&dev->rd_msg_hdr;
354
+ mei_hdr = (struct mei_msg_hdr *)dev->rd_msg_hdr;
287355 dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
288356
289357 if (mei_slots2data(*slots) < mei_hdr->length) {
....@@ -292,6 +360,56 @@
292360 /* we can't read the message */
293361 ret = -ENODATA;
294362 goto end;
363
+ }
364
+
365
+ ext_hdr_end = 1;
366
+ hdr_size_left = mei_hdr->length;
367
+
368
+ if (mei_hdr->extended) {
369
+ if (!dev->rd_msg_hdr[1]) {
370
+ ext_meta_hdr_u32 = mei_read_hdr(dev);
371
+ dev->rd_msg_hdr[1] = ext_meta_hdr_u32;
372
+ dev->rd_msg_hdr_count++;
373
+ (*slots)--;
374
+ dev_dbg(dev->dev, "extended header is %08x\n",
375
+ ext_meta_hdr_u32);
376
+ }
377
+ meta_hdr = ((struct mei_ext_meta_hdr *)dev->rd_msg_hdr + 1);
378
+ if (check_add_overflow((u32)sizeof(*meta_hdr),
379
+ mei_slots2data(meta_hdr->size),
380
+ &hdr_size_ext)) {
381
+ dev_err(dev->dev, "extended message size too big %d\n",
382
+ meta_hdr->size);
383
+ return -EBADMSG;
384
+ }
385
+ if (hdr_size_left < hdr_size_ext) {
386
+ dev_err(dev->dev, "corrupted message header len %d\n",
387
+ mei_hdr->length);
388
+ return -EBADMSG;
389
+ }
390
+ hdr_size_left -= hdr_size_ext;
391
+
392
+ ext_hdr_end = meta_hdr->size + 2;
393
+ for (i = dev->rd_msg_hdr_count; i < ext_hdr_end; i++) {
394
+ dev->rd_msg_hdr[i] = mei_read_hdr(dev);
395
+ dev_dbg(dev->dev, "extended header %d is %08x\n", i,
396
+ dev->rd_msg_hdr[i]);
397
+ dev->rd_msg_hdr_count++;
398
+ (*slots)--;
399
+ }
400
+ }
401
+
402
+ if (mei_hdr->dma_ring) {
403
+ if (hdr_size_left != sizeof(dev->rd_msg_hdr[ext_hdr_end])) {
404
+ dev_err(dev->dev, "corrupted message header len %d\n",
405
+ mei_hdr->length);
406
+ return -EBADMSG;
407
+ }
408
+
409
+ dev->rd_msg_hdr[ext_hdr_end] = mei_read_hdr(dev);
410
+ dev->rd_msg_hdr_count++;
411
+ (*slots)--;
412
+ mei_hdr->length -= sizeof(dev->rd_msg_hdr[ext_hdr_end]);
295413 }
296414
297415 /* HBM message */
....@@ -309,37 +427,32 @@
309427 list_for_each_entry(cl, &dev->file_list, link) {
310428 if (mei_cl_hbm_equal(cl, mei_hdr)) {
311429 cl_dbg(dev, cl, "got a message\n");
312
- break;
430
+ ret = mei_cl_irq_read_msg(cl, mei_hdr, meta_hdr, cmpl_list);
431
+ goto reset_slots;
313432 }
314433 }
315434
316435 /* if no recipient cl was found we assume corrupted header */
317
- if (&cl->link == &dev->file_list) {
318
- /* A message for not connected fixed address clients
319
- * should be silently discarded
320
- * On power down client may be force cleaned,
321
- * silently discard such messages
322
- */
323
- if (hdr_is_fixed(mei_hdr) ||
324
- dev->dev_state == MEI_DEV_POWER_DOWN) {
325
- mei_irq_discard_msg(dev, mei_hdr);
326
- ret = 0;
327
- goto reset_slots;
328
- }
329
- dev_err(dev->dev, "no destination client found 0x%08X\n",
330
- dev->rd_msg_hdr);
331
- ret = -EBADMSG;
332
- goto end;
436
+ /* A message for not connected fixed address clients
437
+ * should be silently discarded
438
+ * On power down client may be force cleaned,
439
+ * silently discard such messages
440
+ */
441
+ if (hdr_is_fixed(mei_hdr) ||
442
+ dev->dev_state == MEI_DEV_POWER_DOWN) {
443
+ mei_irq_discard_msg(dev, mei_hdr, mei_hdr->length);
444
+ ret = 0;
445
+ goto reset_slots;
333446 }
334
-
335
- ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
336
-
447
+ dev_err(dev->dev, "no destination client found 0x%08X\n", dev->rd_msg_hdr[0]);
448
+ ret = -EBADMSG;
449
+ goto end;
337450
338451 reset_slots:
339452 /* reset the number of slots and header */
453
+ memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
454
+ dev->rd_msg_hdr_count = 0;
340455 *slots = mei_count_full_read_slots(dev);
341
- dev->rd_msg_hdr = 0;
342
-
343456 if (*slots == -EOVERFLOW) {
344457 /* overflow - reset */
345458 dev_err(dev->dev, "resetting due to slots overflow.\n");