1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * Part of Intel(R) Manageability Engine Interface Linux driver
8 *
9 * Copyright (c) 2003 - 2008 Intel Corp.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions, and the following disclaimer,
17 * without modification.
18 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19 * substantially similar to the "NO WARRANTY" disclaimer below
20 * ("Disclaimer") and any redistribution must be conditioned upon
21 * including a substantially similar Disclaimer requirement for further
22 * binary redistribution.
23 * 3. Neither the names of the above-listed copyright holders nor the names
24 * of any contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * Alternatively, this software may be distributed under the terms of the
28 * GNU General Public License ("GPL") version 2 as published by the Free
29 * Software Foundation.
30 *
31 * NO WARRANTY
32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGES.
43 *
44 */
45
46 #include <sys/types.h>
47 #include <sys/cmn_err.h>
48 #include <sys/conf.h>
49 #include <sys/ddi.h>
50 #include <sys/ddi_impldefs.h>
51 #include <sys/devops.h>
52 #include <sys/instance.h>
53 #include <sys/modctl.h>
54 #include <sys/open.h>
55 #include <sys/stat.h>
56 #include <sys/sunddi.h>
57 #include <sys/sunndi.h>
58 #include <sys/systm.h>
59 #include <sys/mkdev.h>
60 #include <sys/list.h>
61 #include <sys/note.h>
62 #include "heci_data_structures.h"
63 #include "heci_interface.h"
64 #include "heci.h"
65
66
67 const uint8_t watch_dog_data[] = {
68 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
69 };
70 const uint8_t start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
71 const uint8_t stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
72
73 const uint8_t heci_wd_state_independence_msg[3][4] = {
74 {0x05, 0x02, 0x51, 0x10},
75 {0x05, 0x02, 0x52, 0x10},
76 {0x07, 0x02, 0x01, 0x10}
77 };
78
79 const struct guid heci_asf_guid = {
80 0x75B30CD6, 0xA29E, 0x4AF7,
81 {0xA7, 0x12, 0xE6, 0x17, 0x43, 0x93, 0xC8, 0xA6}
82 };
83 const struct guid heci_wd_guid = {
84 0x05B79A6F, 0x4628, 0x4D7F,
85 {0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB}
86 };
87 const struct guid heci_pthi_guid = {
88 0x12f80028, 0xb4b7, 0x4b2d,
89 {0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c}
90 };
91
92
93 /*
94 * heci init function prototypes
95 */
96 static void heci_check_asf_mode(struct iamt_heci_device *dev);
97 static int host_start_message(struct iamt_heci_device *dev);
98 static int host_enum_clients_message(struct iamt_heci_device *dev);
99 static int allocate_me_clients_storage(struct iamt_heci_device *dev);
100 static void host_init_wd(struct iamt_heci_device *dev);
101 static void host_init_iamthif(struct iamt_heci_device *dev);
102 static inline int heci_fe_same_id(struct heci_file_private *fe1,
103 struct heci_file_private *fe2);
104
105
106
107 /*
108 * heci_initialize_list - Sets up a queue list.
109 *
110 * @list: An instance of our list structure
111 * @dev: Device object for our driver
112 */
113 void
heci_initialize_list(struct io_heci_list * list,struct iamt_heci_device * dev)114 heci_initialize_list(struct io_heci_list *list,
115 struct iamt_heci_device *dev)
116 {
117 /* initialize our queue list */
118 LIST_INIT_HEAD(&list->heci_cb.cb_list);
119 list->status = 0;
120 list->device_extension = dev;
121 }
122
123 /*
124 * heci_flush_queues - flush our queues list belong to file_ext.
125 *
126 * @dev: Device object for our driver
127 * @file_ext: private data of the file object
128 *
129 */
130 void
heci_flush_queues(struct iamt_heci_device * dev,struct heci_file_private * file_ext)131 heci_flush_queues(struct iamt_heci_device *dev,
132 struct heci_file_private *file_ext)
133 {
134 int i;
135
136 if (!dev || !file_ext)
137 return;
138
139 /* flush our queue list belong to file_ext */
140 for (i = 0; i < HECI_IO_LISTS_NUMBER; i++) {
141 DBG("remove list entry belong to file_ext\n");
142 heci_flush_list(dev->io_list_array[i], file_ext);
143 }
144 }
145
146
147 /*
148 * heci_flush_list - remove list entry belong to file_ext.
149 *
150 * @list: An instance of our list structure
151 * @file_ext: private data of the file object
152 */
153 void
heci_flush_list(struct io_heci_list * list,struct heci_file_private * file_ext)154 heci_flush_list(struct io_heci_list *list,
155 struct heci_file_private *file_ext)
156 {
157 struct heci_file_private *file_ext_tmp;
158 struct heci_cb_private *priv_cb_pos = NULL;
159 struct heci_cb_private *priv_cb_next = NULL;
160
161 if (!list || !file_ext)
162 return;
163
164 if (list->status != 0)
165 return;
166
167 if (list_empty(&list->heci_cb.cb_list))
168 return;
169
170 list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
171 &list->heci_cb.cb_list, cb_list, struct heci_cb_private) {
172 if (priv_cb_pos) {
173 file_ext_tmp = (struct heci_file_private *)
174 priv_cb_pos->file_private;
175 if (file_ext_tmp) {
176 if (heci_fe_same_id(file_ext, file_ext_tmp))
177 list_del(&priv_cb_pos->cb_list);
178 }
179 }
180 }
181 }
182
183 /*
184 * heci_reset_iamthif_params - initializes heci device iamthif
185 * @dev: The heci device structure
186 */
heci_reset_iamthif_params(struct iamt_heci_device * dev)187 static void heci_reset_iamthif_params(struct iamt_heci_device *dev)
188 {
189 /* reset iamthif parameters. */
190 dev->iamthif_current_cb = NULL;
191 dev->iamthif_msg_buf_size = 0;
192 dev->iamthif_msg_buf_index = 0;
193 dev->iamthif_canceled = 0;
194 dev->iamthif_file_ext.file = NULL;
195 dev->iamthif_ioctl = 0;
196 dev->iamthif_state = HECI_IAMTHIF_IDLE;
197 dev->iamthif_timer = 0;
198 }
199
200 /*
201 * fini_heci_device - release resources allocated in init_heci_device()
202 */
203 void
fini_heci_device(struct iamt_heci_device * device)204 fini_heci_device(struct iamt_heci_device *device)
205 {
206 mutex_destroy(&device->device_lock);
207 cv_destroy(&device->wait_recvd_msg);
208 cv_destroy(&device->wait_stop_wd);
209 if (device->work)
210 ddi_taskq_destroy(device->work);
211 if (device->reinit_tsk)
212 ddi_taskq_destroy(device->reinit_tsk);
213
214 }
215
216 /*
217 * init_heci_device - initializes the heci device structure
218 *
219 */
220 void
init_heci_device(dev_info_t * dip,struct iamt_heci_device * device)221 init_heci_device(dev_info_t *dip,
222 struct iamt_heci_device *device)
223 {
224 int i;
225
226 if (!device)
227 return;
228
229 /* setup our list array */
230 device->io_list_array[0] = &device->read_list;
231 device->io_list_array[1] = &device->write_list;
232 device->io_list_array[2] = &device->write_waiting_list;
233 device->io_list_array[3] = &device->ctrl_wr_list;
234 device->io_list_array[4] = &device->ctrl_rd_list;
235 device->io_list_array[5] = &device->pthi_cmd_list;
236 device->io_list_array[6] = &device->pthi_read_complete_list;
237 LIST_INIT_HEAD(&device->file_list);
238 LIST_INIT_HEAD(&device->wd_file_ext.link);
239 LIST_INIT_HEAD(&device->iamthif_file_ext.link);
240 mutex_init(&device->device_lock, NULL, MUTEX_DRIVER, NULL);
241 cv_init(&device->wait_recvd_msg, NULL, CV_DRIVER, NULL);
242 cv_init(&device->wait_stop_wd, NULL, CV_DRIVER, NULL);
243 device->open_handle_count = 0;
244 device->num_heci_me_clients = 0;
245 device->extra_write_index = 0;
246 device->rd_msg_hdr = 0;
247 device->mem_addr = NULL;
248 device->asf_mode = B_FALSE;
249 device->need_reset = B_FALSE;
250 device->recvd_msg = B_FALSE;
251 device->heci_state = HECI_INITIALIZING;
252 device->iamthif_state = HECI_IAMTHIF_IDLE;
253
254 device->work = ddi_taskq_create(dip, "heci_bh_handler", 1,
255 TASKQ_DEFAULTPRI, 0);
256 if (device->work == NULL)
257 cmn_err(CE_WARN, "taskq_create failed for heci_bh_handler");
258 device->reinit_tsk = ddi_taskq_create(dip, "heci_reinit_tsk", 1,
259 TASKQ_DEFAULTPRI, 0);
260 if (device->reinit_tsk == NULL)
261 cmn_err(CE_WARN, "taskq_create failed for reinit_tsk");
262
263 device->wd_pending = B_FALSE;
264 device->wd_stoped = B_FALSE;
265
266 device->me_clients = NULL;
267 for (i = 0; i < HECI_IO_LISTS_NUMBER; i++)
268 heci_initialize_list(device->io_list_array[i], device);
269 device->dip = dip;
270 }
271
272 /*
273 * heci_hw_init - init host and fw to start work.
274 *
275 * @dev: Device object for our driver
276 *
277 * @return 0 on success, <0 on failure.
278 */
279 int
heci_hw_init(struct iamt_heci_device * dev)280 heci_hw_init(struct iamt_heci_device *dev)
281 {
282 int err = 0;
283
284 mutex_enter(&dev->device_lock);
285 dev->host_hw_state = read_heci_register(dev, H_CSR);
286 dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
287 DBG("host_hw_state = 0x%08x, mestate = 0x%08x.\n",
288 dev->host_hw_state, dev->me_hw_state);
289
290 if ((dev->host_hw_state & H_IS) == H_IS) {
291 /* acknowledge interrupt and stop interupts */
292 heci_set_csr_register(dev);
293 }
294 dev->recvd_msg = 0;
295 DBG("reset in start the heci device.\n");
296
297 heci_reset(dev, 1);
298
299 DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
300 dev->host_hw_state, dev->me_hw_state);
301
302 /* wait for ME to turn on ME_RDY */
303 err = 0;
304 while (!dev->recvd_msg && err != -1) {
305 err = cv_reltimedwait(&dev->wait_recvd_msg,
306 &dev->device_lock, HECI_INTEROP_TIMEOUT, TR_CLOCK_TICK);
307 }
308
309 if (err == -1 && !dev->recvd_msg) {
310 dev->heci_state = HECI_DISABLED;
311 DBG("wait_event_interruptible_timeout failed"
312 "on wait for ME to turn on ME_RDY.\n");
313 mutex_exit(&dev->device_lock);
314 return (-ENODEV);
315 } else {
316 if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
317 ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
318 dev->heci_state = HECI_DISABLED;
319 DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
320 dev->host_hw_state,
321 dev->me_hw_state);
322
323 if (!(dev->host_hw_state & H_RDY) != H_RDY)
324 DBG("host turn off H_RDY.\n");
325
326 if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
327 DBG("ME turn off ME_RDY.\n");
328
329 cmn_err(CE_WARN,
330 "heci: link layer initialization failed.\n");
331 mutex_exit(&dev->device_lock);
332 return (-ENODEV);
333 }
334 }
335 dev->recvd_msg = 0;
336 DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
337 dev->host_hw_state, dev->me_hw_state);
338 DBG("ME turn on ME_RDY and host turn on H_RDY.\n");
339 DBG("heci: link layer has been established.\n");
340 mutex_exit(&dev->device_lock);
341 return (0);
342 }
343
344 /*
345 * heci_hw_reset - reset fw via heci csr register.
346 *
347 * @dev: Device object for our driver
348 * @interrupts: if interrupt should be enable after reset.
349 */
heci_hw_reset(struct iamt_heci_device * dev,int interrupts)350 static void heci_hw_reset(struct iamt_heci_device *dev, int interrupts)
351 {
352 dev->host_hw_state |= (H_RST | H_IG);
353
354 if (interrupts)
355 heci_csr_enable_interrupts(dev);
356 else
357 heci_csr_disable_interrupts(dev);
358
359 }
360
361 /*
362 * heci_reset - reset host and fw.
363 *
364 * @dev: Device object for our driver
365 * @interrupts: if interrupt should be enable after reset.
366 */
367 void
heci_reset(struct iamt_heci_device * dev,int interrupts)368 heci_reset(struct iamt_heci_device *dev, int interrupts)
369 {
370 struct heci_file_private *file_pos = NULL;
371 struct heci_file_private *file_next = NULL;
372 struct heci_cb_private *priv_cb_pos = NULL;
373 struct heci_cb_private *priv_cb_next = NULL;
374 int unexpected = 0;
375
376 if (dev->heci_state == HECI_RECOVERING_FROM_RESET) {
377 dev->need_reset = 1;
378 return;
379 }
380
381 if (dev->heci_state != HECI_INITIALIZING &&
382 dev->heci_state != HECI_DISABLED &&
383 dev->heci_state != HECI_POWER_DOWN &&
384 dev->heci_state != HECI_POWER_UP)
385 unexpected = 1;
386
387 if (dev->reinit_tsk != NULL) {
388 mutex_exit(&dev->device_lock);
389 (void) ddi_taskq_wait(dev->reinit_tsk);
390 mutex_enter(&dev->device_lock);
391 }
392
393 dev->host_hw_state = read_heci_register(dev, H_CSR);
394
395 DBG("before reset host_hw_state = 0x%08x.\n",
396 dev->host_hw_state);
397
398 heci_hw_reset(dev, interrupts);
399
400 dev->host_hw_state &= ~H_RST;
401 dev->host_hw_state |= H_IG;
402
403 write_heci_register(dev, H_CSR, dev->host_hw_state);
404
405 DBG("currently saved host_hw_state = 0x%08x.\n",
406 dev->host_hw_state);
407
408 dev->need_reset = 0;
409
410 if (dev->heci_state != HECI_INITIALIZING) {
411 if ((dev->heci_state != HECI_DISABLED) &&
412 (dev->heci_state != HECI_POWER_DOWN))
413 dev->heci_state = HECI_RESETING;
414
415 list_for_each_entry_safe(file_pos,
416 file_next, &dev->file_list, link,
417 struct heci_file_private) {
418 file_pos->state = HECI_FILE_DISCONNECTED;
419 file_pos->flow_ctrl_creds = 0;
420 file_pos->read_cb = NULL;
421 file_pos->timer_count = 0;
422 }
423 /* remove entry if already in list */
424 DBG("list del iamthif and wd file list.\n");
425 heci_remove_client_from_file_list(dev,
426 dev->wd_file_ext.host_client_id);
427
428 heci_remove_client_from_file_list(dev,
429 dev->iamthif_file_ext.host_client_id);
430
431 heci_reset_iamthif_params(dev);
432 dev->wd_due_counter = 0;
433 dev->extra_write_index = 0;
434 }
435
436 dev->num_heci_me_clients = 0;
437 dev->rd_msg_hdr = 0;
438 dev->stop = 0;
439 dev->wd_pending = 0;
440
441 /* update the state of the registers after reset */
442 dev->host_hw_state = read_heci_register(dev, H_CSR);
443 dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
444
445 DBG("after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
446 dev->host_hw_state, dev->me_hw_state);
447
448 if (unexpected)
449 cmn_err(CE_WARN, "unexpected heci reset.\n");
450
451 /* Wake up all readings so they can be interrupted */
452 list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
453 struct heci_file_private) {
454 cmn_err(CE_NOTE, "heci: Waking up client!\n");
455 cv_broadcast(&file_pos->rx_wait);
456 }
457 /* remove all waiting requests */
458 if (dev->write_list.status == 0 &&
459 !list_empty(&dev->write_list.heci_cb.cb_list)) {
460 list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
461 &dev->write_list.heci_cb.cb_list, cb_list,
462 struct heci_cb_private) {
463 if (priv_cb_pos) {
464 list_del(&priv_cb_pos->cb_list);
465 heci_free_cb_private(priv_cb_pos);
466 }
467 }
468 }
469 }
470
471 /*
472 * heci_initialize_clients - routine.
473 *
474 * @dev: Device object for our driver
475 *
476 */
477 int
heci_initialize_clients(struct iamt_heci_device * dev)478 heci_initialize_clients(struct iamt_heci_device *dev)
479 {
480 int status;
481
482 /* msleep(100) FW needs time to be ready to talk with us */
483 delay(drv_usectohz(100000));
484 DBG("link is established start sending messages.\n");
485 /* link is established start sending messages. */
486 status = host_start_message(dev);
487 if (status != 0) {
488 mutex_enter(&dev->device_lock);
489 dev->heci_state = HECI_DISABLED;
490 mutex_exit(&dev->device_lock);
491 DBG("start sending messages failed.\n");
492 return (status);
493 }
494 /* enumerate clients */
495
496 status = host_enum_clients_message(dev);
497 if (status != 0) {
498 mutex_enter(&dev->device_lock);
499 dev->heci_state = HECI_DISABLED;
500 mutex_exit(&dev->device_lock);
501 DBG("enum clients failed.\n");
502 return (status);
503 }
504 /* allocate storage for ME clients representation */
505 status = allocate_me_clients_storage(dev);
506 if (status != 0) {
507 mutex_enter(&dev->device_lock);
508 dev->num_heci_me_clients = 0;
509 dev->heci_state = HECI_DISABLED;
510 mutex_exit(&dev->device_lock);
511 DBG("allocate clients failed.\n");
512 return (status);
513 }
514
515 heci_check_asf_mode(dev);
516 /* heci initialization wd */
517 host_init_wd(dev);
518 /* heci initialization iamthif client */
519 host_init_iamthif(dev);
520
521 mutex_enter(&dev->device_lock);
522 if (dev->need_reset) {
523 dev->need_reset = 0;
524 dev->heci_state = HECI_DISABLED;
525 mutex_exit(&dev->device_lock);
526 return (-ENODEV);
527 }
528
529 (void) memset(dev->heci_host_clients, 0,
530 sizeof (dev->heci_host_clients));
531 dev->open_handle_count = 0;
532 dev->heci_host_clients[0] |= 7;
533 dev->current_host_client_id = 3;
534 dev->heci_state = HECI_ENABLED;
535 mutex_exit(&dev->device_lock);
536 DBG("initialization heci clients successful.\n");
537 return (0);
538 }
539
540 /*
541 * heci_task_initialize_clients - routine.
542 *
543 * @data: Device object for our driver
544 *
545 */
546 void
heci_task_initialize_clients(void * data)547 heci_task_initialize_clients(void *data)
548 {
549 int ret;
550 struct iamt_heci_device *dev = (struct iamt_heci_device *)data;
551
552 ret = heci_initialize_clients(dev);
553 if (ret)
554 cmn_err(CE_WARN, "heci_initialize_clients() failed\n");
555 }
556
557 /*
558 * host_start_message - heci host send start message.
559 *
560 * @dev: Device object for our driver
561 *
562 * @return 0 on success, <0 on failure.
563 */
564 static int
host_start_message(struct iamt_heci_device * dev)565 host_start_message(struct iamt_heci_device *dev)
566 {
567 long timeout = 60; /* 60 second */
568 struct heci_msg_hdr *heci_hdr;
569 struct hbm_host_version_request *host_start_req;
570 struct hbm_host_stop_request *host_stop_req;
571 int err = 0;
572 clock_t delta = (clock_t)(timeout * HZ);
573
574 /* host start message */
575 mutex_enter(&dev->device_lock);
576 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
577 heci_hdr->host_addr = 0;
578 heci_hdr->me_addr = 0;
579 heci_hdr->length = sizeof (struct hbm_host_version_request);
580 heci_hdr->msg_complete = 1;
581 heci_hdr->reserved = 0;
582
583 host_start_req =
584 (struct hbm_host_version_request *)&dev->wr_msg_buf[1];
585 (void) memset(host_start_req, 0,
586 sizeof (struct hbm_host_version_request));
587 host_start_req->cmd.cmd = HOST_START_REQ_CMD;
588 host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
589 host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
590 dev->recvd_msg = 0;
591 if (!heci_write_message(dev, heci_hdr,
592 (unsigned char *)(host_start_req),
593 heci_hdr->length)) {
594 DBG("send version to fw fail.\n");
595 mutex_exit(&dev->device_lock);
596 return (-ENODEV);
597 }
598 DBG("call wait_event_interruptible_timeout for response message.\n");
599 err = 0;
600 while (err != -1 && !dev->recvd_msg) {
601 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
602 delta, TR_CLOCK_TICK);
603 }
604 if (err == -1 && !dev->recvd_msg) {
605 DBG("wait_timeout failed on host start response message.\n");
606 mutex_exit(&dev->device_lock);
607 return (-ENODEV);
608 }
609 dev->recvd_msg = 0;
610 DBG("wait_timeout successful on host start response message.\n");
611 if ((dev->version.major_version != HBM_MAJOR_VERSION) ||
612 (dev->version.minor_version != HBM_MINOR_VERSION)) {
613 /* send stop message */
614 heci_hdr->host_addr = 0;
615 heci_hdr->me_addr = 0;
616 heci_hdr->length = sizeof (struct hbm_host_stop_request);
617 heci_hdr->msg_complete = 1;
618 heci_hdr->reserved = 0;
619
620 host_stop_req =
621 (struct hbm_host_stop_request *)&dev->wr_msg_buf[1];
622
623 (void) memset(host_stop_req, 0,
624 sizeof (struct hbm_host_stop_request));
625 host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
626 host_stop_req->reason = DRIVER_STOP_REQUEST;
627 if (!heci_write_message(dev, heci_hdr,
628 (unsigned char *)(host_stop_req),
629 heci_hdr->length)) {
630 DBG("sending stop msg to fw failed.\n");
631 }
632 DBG("version mismatch.\n");
633 mutex_exit(&dev->device_lock);
634 return (-ENODEV);
635 }
636 mutex_exit(&dev->device_lock);
637 return (0);
638 }
639
640 /*
641 * host_enum_clients_message - host send enumeration client request message.
642 *
643 * @dev: Device object for our driver
644 * @return 0 on success, <0 on failure.
645 */
646 static int
host_enum_clients_message(struct iamt_heci_device * dev)647 host_enum_clients_message(struct iamt_heci_device *dev)
648 {
649 long timeout = 5; /* 5 second */
650 struct heci_msg_hdr *heci_hdr;
651 struct hbm_host_enum_request *host_enum_req;
652 int err = 0;
653 uint8_t i, j;
654 clock_t delta = (clock_t)(timeout * HZ);
655
656 mutex_enter(&dev->device_lock);
657
658 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
659 /* enumerate clients */
660 heci_hdr->host_addr = 0;
661 heci_hdr->me_addr = 0;
662 heci_hdr->length = sizeof (struct hbm_host_enum_request);
663 heci_hdr->msg_complete = 1;
664 heci_hdr->reserved = 0;
665
666 host_enum_req = (struct hbm_host_enum_request *)&dev->wr_msg_buf[1];
667 (void) memset(host_enum_req, 0, sizeof (struct hbm_host_enum_request));
668 host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD;
669 if (!heci_write_message(dev, heci_hdr,
670 (unsigned char *)(host_enum_req),
671 heci_hdr->length)) {
672 DBG("send enumeration request failed.\n");
673 mutex_exit(&dev->device_lock);
674 return (-ENODEV);
675 }
676 /* wait for response */
677 dev->recvd_msg = 0;
678 err = 0;
679 while (!dev->recvd_msg && err != -1) {
680 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
681 delta, TR_CLOCK_TICK);
682 }
683 if (err == -1 && !dev->recvd_msg) {
684 DBG("wait_event_interruptible_timeout failed "
685 "on enumeration clients response message.\n");
686 mutex_exit(&dev->device_lock);
687 return (-ENODEV);
688 }
689 dev->recvd_msg = 0;
690
691 /* count how many ME clients we have */
692 for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
693 for (j = 0; j < 8; j++) {
694 if ((dev->heci_me_clients[i] & (1 << j)) != 0)
695 dev->num_heci_me_clients++;
696
697 }
698 }
699 mutex_exit(&dev->device_lock);
700
701 return (0);
702 }
703
704 /*
705 * host_client_properties - reads properties for client
706 *
707 * @dev: Device object for our driver
708 * @idx: client index in me client array
709 * @client_id: id of the client
710 *
711 * @return 0 on success, <0 on failure.
712 */
713 static int
host_client_properties(struct iamt_heci_device * dev,struct heci_me_client * client)714 host_client_properties(struct iamt_heci_device *dev,
715 struct heci_me_client *client)
716 {
717 struct heci_msg_hdr *heci_hdr;
718 struct hbm_props_request *host_cli_req;
719 int err;
720 clock_t delta = 10 * HZ;
721
722 mutex_enter(&dev->device_lock);
723 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
724 heci_hdr->host_addr = 0;
725 heci_hdr->me_addr = 0;
726 heci_hdr->length = sizeof (struct hbm_props_request);
727 heci_hdr->msg_complete = 1;
728 heci_hdr->reserved = 0;
729
730 host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
731 (void) memset(host_cli_req, 0, sizeof (struct hbm_props_request));
732 host_cli_req->cmd.cmd = HOST_CLIENT_PROPERTEIS_REQ_CMD;
733 host_cli_req->address = client->client_id;
734 if (!heci_write_message(dev, heci_hdr,
735 (unsigned char *)(host_cli_req), heci_hdr->length)) {
736 DBG("send props request failed.\n");
737 mutex_exit(&dev->device_lock);
738 return (-ENODEV);
739 }
740 /* wait for response */
741 dev->recvd_msg = 0;
742
743 err = 0;
744 while (!dev->recvd_msg && err != -1) {
745 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
746 delta, TR_CLOCK_TICK);
747 }
748 if (err == -1 && !dev->recvd_msg) {
749 DBG("wait failed on props resp msg.\n");
750 mutex_exit(&dev->device_lock);
751 return (-ENODEV);
752 }
753 dev->recvd_msg = 0;
754 mutex_exit(&dev->device_lock);
755 return (0);
756 }
757
758 /*
759 * allocate_me_clients_storage - allocate storage for me clients
760 *
761 * @dev: Device object for our driver
762 *
763 * @return 0 on success, <0 on failure.
764 */
765 static int
allocate_me_clients_storage(struct iamt_heci_device * dev)766 allocate_me_clients_storage(struct iamt_heci_device *dev)
767 {
768 struct heci_me_client *clients;
769 struct heci_me_client *client;
770 uint8_t num, i, j;
771 int err;
772
773 if (dev->num_heci_me_clients == 0)
774 return (0);
775
776 mutex_enter(&dev->device_lock);
777 if (dev->me_clients) {
778 kmem_free(dev->me_clients, dev->num_heci_me_clients*
779 sizeof (struct heci_me_client));
780 dev->me_clients = NULL;
781 }
782 mutex_exit(&dev->device_lock);
783
784 /* allocate storage for ME clients representation */
785 clients = kmem_zalloc(dev->num_heci_me_clients*
786 sizeof (struct heci_me_client), KM_SLEEP);
787 if (!clients) {
788 DBG("memory allocation for ME clients failed.\n");
789 return (-ENOMEM);
790 }
791
792 mutex_enter(&dev->device_lock);
793 dev->me_clients = clients;
794 mutex_exit(&dev->device_lock);
795
796 num = 0;
797 for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
798 for (j = 0; j < 8; j++) {
799 if ((dev->heci_me_clients[i] & (1 << j)) != 0) {
800 client = &dev->me_clients[num];
801 client->client_id = (i * 8) + j;
802 client->flow_ctrl_creds = 0;
803 err = host_client_properties(dev, client);
804 if (err != 0) {
805 mutex_enter(&dev->device_lock);
806 kmem_free(dev->me_clients,
807 dev->num_heci_me_clients*
808 sizeof (struct heci_me_client));
809 dev->me_clients = NULL;
810 mutex_exit(&dev->device_lock);
811 return (err);
812 }
813 num++;
814 }
815 }
816 }
817
818 return (0);
819 }
820
821 /*
822 * heci_init_file_private - initializes private file structure.
823 *
824 * @priv: private file structure to be initialized
825 * @file: the file structure
826 *
827 */
828 static void
heci_init_file_private(struct heci_file_private * priv,struct heci_file * file)829 heci_init_file_private(struct heci_file_private *priv,
830 struct heci_file *file)
831 {
832 _NOTE(ARGUNUSED(file));
833
834 (void) memset(priv, 0, sizeof (struct heci_file_private));
835 mutex_init(&priv->file_lock, NULL, MUTEX_DRIVER, NULL);
836 mutex_init(&priv->read_io_lock, NULL, MUTEX_DRIVER, NULL);
837 mutex_init(&priv->write_io_lock, NULL, MUTEX_DRIVER, NULL);
838 cv_init(&priv->rx_wait, NULL, CV_DRIVER, NULL);
839 DBG("priv->rx_wait =%p\n", (void *)&priv->rx_wait);
840 LIST_INIT_HEAD(&priv->link);
841 priv->reading_state = HECI_IDLE;
842 priv->writing_state = HECI_IDLE;
843 }
844
845 /*
846 * heci_find_me_client - search for ME client guid
847 * sets client_id in heci_file_private if found
848 * @dev: Device object for our driver
849 * @priv: private file structure to set client_id in
850 * @cguid: searched guid of ME client
851 * @client_id: id of host client to be set in file private structure
852 *
853 * @return ME client index
854 */
855 static uint8_t
heci_find_me_client(struct iamt_heci_device * dev,struct heci_file_private * priv,const struct guid * cguid,uint8_t client_id)856 heci_find_me_client(struct iamt_heci_device *dev,
857 struct heci_file_private *priv,
858 const struct guid *cguid, uint8_t client_id)
859 {
860 uint8_t i;
861
862 if ((dev == NULL) || (priv == NULL) || (cguid == NULL))
863 return (0);
864
865 for (i = 0; i < dev->num_heci_me_clients; i++) {
866 if (memcmp(cguid,
867 &dev->me_clients[i].props.protocol_name,
868 sizeof (struct guid)) == 0) {
869 priv->me_client_id = dev->me_clients[i].client_id;
870 priv->state = HECI_FILE_CONNECTING;
871 priv->host_client_id = client_id;
872
873 list_add_tail(&priv->link, &dev->file_list);
874 return (i);
875 }
876 }
877 return (0);
878 }
879
880 /*
881 * heci_check_asf_mode - check for ASF client
882 *
883 * @dev: Device object for our driver
884 *
885 */
886 static void
heci_check_asf_mode(struct iamt_heci_device * dev)887 heci_check_asf_mode(struct iamt_heci_device *dev)
888 {
889 uint8_t i;
890
891 mutex_enter(&dev->device_lock);
892 dev->asf_mode = 0;
893 /* find ME ASF client - otherwise assume AMT mode */
894 DBG("find ME ASF client - otherwise assume AMT mode.\n");
895 for (i = 0; i < dev->num_heci_me_clients; i++) {
896 if (memcmp(&heci_asf_guid,
897 &dev->me_clients[i].props.protocol_name,
898 sizeof (struct guid)) == 0) {
899 dev->asf_mode = 1;
900 mutex_exit(&dev->device_lock);
901 DBG("found ME ASF client.\n");
902 return;
903 }
904 }
905 mutex_exit(&dev->device_lock);
906 DBG("assume AMT mode.\n");
907 }
908
909 /*
910 * heci_connect_me_client - connect ME client
911 * @dev: Device object for our driver
912 * @priv: private file structure
913 * @timeout: connect timeout in seconds
914 *
915 * @return 1 - if connected, 0 - if not
916 */
917 static uint8_t
heci_connect_me_client(struct iamt_heci_device * dev,struct heci_file_private * priv,long timeout)918 heci_connect_me_client(struct iamt_heci_device *dev,
919 struct heci_file_private *priv,
920 long timeout)
921 {
922 int err = 0;
923 clock_t delta = (clock_t)(timeout * HZ);
924
925 if ((dev == NULL) || (priv == NULL))
926 return (0);
927
928 if (!heci_connect(dev, priv)) {
929 DBG("failed to call heci_connect for client_id=%d.\n",
930 priv->host_client_id);
931 heci_remove_client_from_file_list(dev, priv->host_client_id);
932 priv->state = HECI_FILE_DISCONNECTED;
933 return (0);
934 }
935 err = 0;
936 while (!(HECI_FILE_CONNECTED == priv->state ||
937 HECI_FILE_DISCONNECTED == priv->state) &&
938 err != -1) {
939 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
940 delta, TR_CLOCK_TICK);
941 }
942 if (HECI_FILE_CONNECTED != priv->state) {
943 heci_remove_client_from_file_list(dev, priv->host_client_id);
944 DBG("failed to connect client_id=%d state=%d.\n",
945 priv->host_client_id, priv->state);
946 if (err)
947 DBG("failed connect err=%08x\n", err);
948 priv->state = HECI_FILE_DISCONNECTED;
949 return (0);
950 }
951 DBG("successfully connected client_id=%d.\n",
952 priv->host_client_id);
953 return (1);
954 }
955
956 /*
957 * host_init_wd - heci initialization wd.
958 *
959 * @dev: Device object for our driver
960 *
961 */
host_init_wd(struct iamt_heci_device * dev)962 static void host_init_wd(struct iamt_heci_device *dev)
963 {
964
965 mutex_enter(&dev->device_lock);
966
967 heci_init_file_private(&dev->wd_file_ext, NULL);
968
969 /* look for WD client and connect to it */
970 dev->wd_file_ext.state = HECI_FILE_DISCONNECTED;
971 dev->wd_timeout = 0;
972
973 if (dev->asf_mode) {
974 (void) memcpy(dev->wd_data, stop_wd_params,
975 HECI_WD_PARAMS_SIZE);
976 } else {
977 /* AMT mode */
978 dev->wd_timeout = AMT_WD_VALUE;
979 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
980 (void) memcpy(dev->wd_data, start_wd_params,
981 HECI_WD_PARAMS_SIZE);
982 (void) memcpy(dev->wd_data + HECI_WD_PARAMS_SIZE,
983 &dev->wd_timeout, sizeof (uint16_t));
984 }
985
986 /* find ME WD client */
987 (void) heci_find_me_client(dev, &dev->wd_file_ext,
988 &heci_wd_guid, HECI_WD_HOST_CLIENT_ID);
989
990 DBG("check wd_file_ext\n");
991 if (HECI_FILE_CONNECTING == dev->wd_file_ext.state) {
992 if (heci_connect_me_client(dev, &dev->wd_file_ext, 15) == 1) {
993 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
994 if (dev->wd_timeout != 0)
995 dev->wd_due_counter = 1;
996 else
997 dev->wd_due_counter = 0;
998 DBG("successfully connected to WD client.\n");
999 }
1000 } else
1001 DBG("failed to find WD client.\n");
1002
1003
1004 mutex_exit(&dev->device_lock);
1005 }
1006
1007
1008 /*
1009 * host_init_iamthif - heci initialization iamthif client.
1010 *
1011 * @dev: Device object for our driver
1012 *
1013 */
1014 static void
host_init_iamthif(struct iamt_heci_device * dev)1015 host_init_iamthif(struct iamt_heci_device *dev)
1016 {
1017 uint8_t i;
1018
1019 mutex_enter(&dev->device_lock);
1020
1021 heci_init_file_private(&dev->iamthif_file_ext, NULL);
1022 dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTED;
1023
1024 /* find ME PTHI client */
1025 i = heci_find_me_client(dev, &dev->iamthif_file_ext,
1026 &heci_pthi_guid, HECI_IAMTHIF_HOST_CLIENT_ID);
1027 if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTING) {
1028 DBG("failed to find iamthif client.\n");
1029 mutex_exit(&dev->device_lock);
1030 return;
1031 }
1032
1033 ASSERT(dev->me_clients[i].props.max_msg_length == IAMTHIF_MTU);
1034
1035 if (heci_connect_me_client(dev, &dev->iamthif_file_ext, 15) == 1) {
1036 DBG("connected to iamthif client.\n");
1037 dev->iamthif_state = HECI_IAMTHIF_IDLE;
1038 }
1039 mutex_exit(&dev->device_lock);
1040 }
1041
1042 /*
1043 * heci_alloc_file_private - allocates a private file structure and set it up.
1044 * @file: the file structure
1045 *
1046 * @return The allocated file or NULL on failure
1047 */
1048 struct heci_file_private *
heci_alloc_file_private(struct heci_file * file)1049 heci_alloc_file_private(struct heci_file *file)
1050 {
1051 struct heci_file_private *priv;
1052
1053 priv = kmem_zalloc(sizeof (struct heci_file_private), KM_SLEEP);
1054 if (!priv)
1055 return (NULL);
1056
1057 heci_init_file_private(priv, file);
1058
1059 return (priv);
1060 }
1061
1062 /*
1063 * heci_free_file_private - free a private file structure that were previously
1064 * allocated by heci_alloc_file_private
1065 */
1066 void
heci_free_file_private(struct heci_file_private * priv)1067 heci_free_file_private(struct heci_file_private *priv)
1068 {
1069 mutex_destroy(&priv->file_lock);
1070 mutex_destroy(&priv->read_io_lock);
1071 mutex_destroy(&priv->write_io_lock);
1072 cv_destroy(&priv->rx_wait);
1073 kmem_free(priv, sizeof (struct heci_file_private));
1074
1075 }
1076
1077 /*
1078 * heci_disconnect_host_client - send disconnect message to fw from host
1079 * client.
1080 *
1081 * @dev: Device object for our driver
1082 * @file_ext: private data of the file object
1083 *
1084 * @return 0 on success, <0 on failure.
1085 */
1086 int
heci_disconnect_host_client(struct iamt_heci_device * dev,struct heci_file_private * file_ext)1087 heci_disconnect_host_client(struct iamt_heci_device *dev,
1088 struct heci_file_private *file_ext)
1089 {
1090 int rets, err;
1091 long timeout = 15; /* 15 seconds */
1092 struct heci_cb_private *priv_cb;
1093 clock_t delta = (clock_t)(timeout * HZ);
1094
1095 if ((!dev) || (!file_ext))
1096 return (-ENODEV);
1097
1098 if (file_ext->state != HECI_FILE_DISCONNECTING)
1099 return (0);
1100
1101 priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
1102 if (!priv_cb)
1103 return (-ENOMEM);
1104
1105 LIST_INIT_HEAD(&priv_cb->cb_list);
1106 priv_cb->file_private = file_ext;
1107 priv_cb->major_file_operations = HECI_CLOSE;
1108 mutex_enter(&dev->device_lock);
1109 if (dev->host_buffer_is_empty) {
1110 dev->host_buffer_is_empty = 0;
1111 if (heci_disconnect(dev, file_ext)) {
1112 list_add_tail(&priv_cb->cb_list,
1113 &dev->ctrl_rd_list.heci_cb.cb_list);
1114 } else {
1115 mutex_exit(&dev->device_lock);
1116 rets = -ENODEV;
1117 DBG("failed to call heci_disconnect.\n");
1118 goto free;
1119 }
1120 } else {
1121 DBG("add disconnect cb to control write list\n");
1122 list_add_tail(&priv_cb->cb_list,
1123 &dev->ctrl_wr_list.heci_cb.cb_list);
1124 }
1125
1126 err = 0;
1127 while (err != -1 &&
1128 (HECI_FILE_DISCONNECTED != file_ext->state)) {
1129
1130 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
1131 delta, TR_CLOCK_TICK);
1132 }
1133 mutex_exit(&dev->device_lock);
1134
1135 if (HECI_FILE_DISCONNECTED == file_ext->state) {
1136 rets = 0;
1137 DBG("successfully disconnected from fw client."
1138 " me_client_id:%d, host_client_id:%d\n",
1139 file_ext->me_client_id,
1140 file_ext->host_client_id);
1141 } else {
1142 rets = -ENODEV;
1143 if (HECI_FILE_DISCONNECTED != file_ext->state)
1144 DBG("wrong status client disconnect.\n");
1145
1146 if (err)
1147 DBG("wait failed disconnect err=%08x\n", err);
1148
1149 DBG("failed to disconnect from fw client.\n"
1150 " me_client_id:%d, host_client_id:%d\n",
1151 file_ext->me_client_id,
1152 file_ext->host_client_id);
1153 }
1154
1155 mutex_enter(&dev->device_lock);
1156 heci_flush_list(&dev->ctrl_rd_list, file_ext);
1157 heci_flush_list(&dev->ctrl_wr_list, file_ext);
1158 mutex_exit(&dev->device_lock);
1159 free:
1160 heci_free_cb_private(priv_cb);
1161 return (rets);
1162 }
1163
1164 /*
1165 * heci_remove_client_from_file_list -
1166 * remove file private data from device file list
1167 *
1168 * @dev: Device object for our driver
1169 * @host_client_id: host client id to be removed
1170 *
1171 */
1172 void
heci_remove_client_from_file_list(struct iamt_heci_device * dev,uint8_t host_client_id)1173 heci_remove_client_from_file_list(struct iamt_heci_device *dev,
1174 uint8_t host_client_id)
1175 {
1176 struct heci_file_private *file_pos = NULL;
1177 struct heci_file_private *file_next = NULL;
1178 list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
1179 struct heci_file_private) {
1180 if (host_client_id == file_pos->host_client_id) {
1181 DBG("remove host client = %d, ME client = %d\n",
1182 file_pos->host_client_id,
1183 file_pos->me_client_id);
1184 list_del_init(&file_pos->link);
1185 break;
1186 }
1187 }
1188 }
1189
1190 /*
1191 * heci_fe_same_id - tell if file private data have same id
1192 *
1193 * @fe1: private data of 1. file object
1194 * @fe2: private data of 2. file object
1195 *
1196 * @return !=0 - if ids are the same, 0 - if differ.
1197 */
heci_fe_same_id(struct heci_file_private * fe1,struct heci_file_private * fe2)1198 static inline int heci_fe_same_id(struct heci_file_private *fe1,
1199 struct heci_file_private *fe2)
1200 {
1201 return ((fe1->host_client_id == fe2->host_client_id) &&
1202 (fe1->me_client_id == fe2->me_client_id));
1203 }
1204