1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/param.h>
29 #include <sys/endian.h>
30 #include <sys/stat.h>
31
32 #include <err.h>
33 #include <errno.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include <libusb.h>
41
42 #include "iwmbt_fw.h"
43 #include "iwmbt_hw.h"
44 #include "iwmbt_dbg.h"
45
46 #define XMIN(x, y) ((x) < (y) ? (x) : (y))
47
48 static int
iwmbt_send_fragment(struct libusb_device_handle * hdl,uint8_t fragment_type,const void * data,uint8_t len,int timeout)49 iwmbt_send_fragment(struct libusb_device_handle *hdl,
50 uint8_t fragment_type, const void *data, uint8_t len, int timeout)
51 {
52 int ret, transferred;
53 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
54 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
55
56 memset(buf, 0, sizeof(buf));
57 cmd->opcode = htole16(0xfc09),
58 cmd->length = len + 1,
59 cmd->data[0] = fragment_type;
60 memcpy(cmd->data + 1, data, len);
61
62 ret = libusb_bulk_transfer(hdl,
63 IWMBT_BULK_OUT_ENDPOINT_ADDR,
64 (uint8_t *)cmd,
65 IWMBT_HCI_CMD_SIZE(cmd),
66 &transferred,
67 timeout);
68
69 if (ret < 0 || transferred != (int)IWMBT_HCI_CMD_SIZE(cmd)) {
70 iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
71 libusb_strerror(ret),
72 IWMBT_HCI_CMD_SIZE(cmd));
73 return (-1);
74 }
75
76 ret = libusb_bulk_transfer(hdl,
77 IWMBT_BULK_IN_ENDPOINT_ADDR,
78 buf,
79 sizeof(buf),
80 &transferred,
81 timeout);
82
83 if (ret < 0) {
84 iwmbt_err("libusb_bulk_transfer() failed: err=%s",
85 libusb_strerror(ret));
86 return (-1);
87 }
88
89 return (0);
90 }
91
92 static int
iwmbt_hci_command(struct libusb_device_handle * hdl,struct iwmbt_hci_cmd * cmd,void * event,int size,int * transferred,int timeout)93 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
94 void *event, int size, int *transferred, int timeout)
95 {
96 int ret;
97
98 ret = libusb_control_transfer(hdl,
99 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
100 0,
101 0,
102 0,
103 (uint8_t *)cmd,
104 IWMBT_HCI_CMD_SIZE(cmd),
105 timeout);
106
107 if (ret < 0) {
108 iwmbt_err("libusb_control_transfer() failed: err=%s",
109 libusb_strerror(ret));
110 return (ret);
111 }
112
113 ret = libusb_interrupt_transfer(hdl,
114 IWMBT_INTERRUPT_ENDPOINT_ADDR,
115 event,
116 size,
117 transferred,
118 timeout);
119
120 if (ret < 0)
121 iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
122 libusb_strerror(ret));
123
124 return (ret);
125 }
126
127 int
iwmbt_patch_fwfile(struct libusb_device_handle * hdl,const struct iwmbt_firmware * fw)128 iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
129 const struct iwmbt_firmware *fw)
130 {
131 int ret, transferred;
132 struct iwmbt_firmware fw_job = *fw;
133 uint16_t cmd_opcode;
134 uint8_t cmd_length;
135 struct iwmbt_hci_cmd *cmd_buf;
136 uint8_t evt_code;
137 uint8_t evt_length;
138 uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
139 int activate_patch = 0;
140
141 while (fw_job.len > 0) {
142 if (fw_job.len < 4) {
143 iwmbt_err("Invalid firmware, unexpected EOF in HCI "
144 "command header. Remains=%d", fw_job.len);
145 return (-1);
146 }
147
148 if (fw_job.buf[0] != 0x01) {
149 iwmbt_err("Invalid firmware, expected HCI command (%d)",
150 fw_job.buf[0]);
151 return (-1);
152 }
153
154 /* Advance by one. */
155 fw_job.buf++;
156 fw_job.len--;
157
158 /* Load in the HCI command to perform. */
159 cmd_opcode = le16dec(fw_job.buf);
160 cmd_length = fw_job.buf[2];
161 cmd_buf = (struct iwmbt_hci_cmd *)fw_job.buf;
162
163 iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length);
164
165 /*
166 * If there is a command that loads a patch in the
167 * firmware file, then activate the patch upon success,
168 * otherwise just disable the manufacturer mode.
169 */
170 if (cmd_opcode == 0xfc8e)
171 activate_patch = 1;
172
173 /* Advance by three. */
174 fw_job.buf += 3;
175 fw_job.len -= 3;
176
177 if (fw_job.len < cmd_length) {
178 iwmbt_err("Invalid firmware, unexpected EOF in HCI "
179 "command data. len=%d, remains=%d",
180 cmd_length, fw_job.len);
181 return (-1);
182 }
183
184 /* Advance by data length. */
185 fw_job.buf += cmd_length;
186 fw_job.len -= cmd_length;
187
188 ret = libusb_control_transfer(hdl,
189 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
190 0,
191 0,
192 0,
193 (uint8_t *)cmd_buf,
194 IWMBT_HCI_CMD_SIZE(cmd_buf),
195 IWMBT_HCI_CMD_TIMEOUT);
196
197 if (ret < 0) {
198 iwmbt_err("libusb_control_transfer() failed: err=%s",
199 libusb_strerror(ret));
200 return (-1);
201 }
202
203 /*
204 * Every command has its associated event: data must match
205 * what is recorded in the firmware file. Perform that check
206 * now.
207 */
208
209 while (fw_job.len > 0 && fw_job.buf[0] == 0x02) {
210 /* Is this the end of the file? */
211 if (fw_job.len < 3) {
212 iwmbt_err("Invalid firmware, unexpected EOF in"
213 "event header. remains=%d", fw_job.len);
214 return (-1);
215 }
216
217 /* Advance by one. */
218 fw_job.buf++;
219 fw_job.len--;
220
221 /* Load in the HCI event. */
222 evt_code = fw_job.buf[0];
223 evt_length = fw_job.buf[1];
224
225 /* Advance by two. */
226 fw_job.buf += 2;
227 fw_job.len -= 2;
228
229 /* Prepare HCI event buffer. */
230 memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE);
231
232 iwmbt_debug("event=%04x, len=%02x",
233 evt_code, evt_length);
234
235 if (fw_job.len < evt_length) {
236 iwmbt_err("Invalid firmware, unexpected EOF in"
237 " event data. len=%d, remains=%d",
238 evt_length, fw_job.len);
239 return (-1);
240 }
241
242 ret = libusb_interrupt_transfer(hdl,
243 IWMBT_INTERRUPT_ENDPOINT_ADDR,
244 evt_buf,
245 IWMBT_HCI_MAX_EVENT_SIZE,
246 &transferred,
247 IWMBT_HCI_CMD_TIMEOUT);
248
249 if (ret < 0) {
250 iwmbt_err("libusb_interrupt_transfer() failed:"
251 " err=%s", libusb_strerror(ret));
252 return (-1);
253 }
254
255 if ((int)evt_length + 2 != transferred ||
256 memcmp(evt_buf + 2, fw_job.buf, evt_length) != 0) {
257 iwmbt_err("event does not match firmware");
258 return (-1);
259 }
260
261 /* Advance by data length. */
262 fw_job.buf += evt_length;
263 fw_job.len -= evt_length;
264 }
265 }
266
267 return (activate_patch);
268 }
269
270 int
iwmbt_load_fwfile(struct libusb_device_handle * hdl,const struct iwmbt_firmware * fw,uint32_t * boot_param)271 iwmbt_load_fwfile(struct libusb_device_handle *hdl,
272 const struct iwmbt_firmware *fw, uint32_t *boot_param)
273 {
274 int ready = 0, sent = 0;
275 int ret, transferred;
276 struct iwmbt_hci_cmd *cmd;
277 struct iwmbt_hci_event *event;
278 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
279
280 #define IWMBT_SEND_FRAGMENT(fragment_type, size, msg) do { \
281 iwmbt_debug("transferring %d bytes, offset %d", size, sent); \
282 \
283 ret = iwmbt_send_fragment(hdl, \
284 fragment_type, \
285 fw->buf + sent, \
286 XMIN(size, fw->len - sent), \
287 IWMBT_HCI_CMD_TIMEOUT); \
288 \
289 if (ret < 0) { \
290 iwmbt_debug("Failed to send "msg": code=%d", ret); \
291 return (-1); \
292 } \
293 sent += size; \
294 } while (0)
295
296 if (fw->len < 644) {
297 iwmbt_err("Invalid size of firmware file (%d)", fw->len);
298 return (-1);
299 }
300
301 iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
302
303 IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
304 IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
305 IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2");
306
307 /* skip 4 bytes */
308 sent += 4;
309
310 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
311 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
312
313 /*
314 * Send firmware chunks. Chunk len must be 4 byte aligned.
315 * multiple commands can be combined
316 */
317 while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) {
318 cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready);
319 /* Parse firmware for Intel Reset HCI command parameter */
320 if (cmd->opcode == htole16(0xfc0e)) {
321 *boot_param = le32dec(cmd->data);
322 iwmbt_debug("boot_param=0x%08x", *boot_param);
323 }
324 ready += IWMBT_HCI_CMD_SIZE(cmd);
325 while (ready >= 0xFC) {
326 IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
327 ready -= 0xFC;
328 }
329 if (ready > 0 && ready % 4 == 0) {
330 IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
331 ready = 0;
332 }
333 }
334
335 /* Wait for firmware download completion event */
336 ret = libusb_interrupt_transfer(hdl,
337 IWMBT_INTERRUPT_ENDPOINT_ADDR,
338 buf,
339 sizeof(buf),
340 &transferred,
341 IWMBT_LOADCMPL_TIMEOUT);
342
343 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
344 iwmbt_err("libusb_interrupt_transfer() failed: "
345 "err=%s, size=%d",
346 libusb_strerror(ret),
347 transferred);
348 return (-1);
349 }
350
351 /* Expect Vendor Specific Event 0x06 */
352 event = (struct iwmbt_hci_event *)buf;
353 if (event->header.event != 0xFF || event->data[0] != 0x06) {
354 iwmbt_err("firmware download completion event missed");
355 return (-1);
356 }
357
358 return (0);
359 }
360
361 int
iwmbt_enter_manufacturer(struct libusb_device_handle * hdl)362 iwmbt_enter_manufacturer(struct libusb_device_handle *hdl)
363 {
364 int ret, transferred;
365 static struct iwmbt_hci_cmd cmd = {
366 .opcode = htole16(0xfc11),
367 .length = 2,
368 .data = { 0x01, 0x00 },
369 };
370 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
371
372 ret = iwmbt_hci_command(hdl,
373 &cmd,
374 buf,
375 sizeof(buf),
376 &transferred,
377 IWMBT_HCI_CMD_TIMEOUT);
378
379 if (ret < 0) {
380 iwmbt_debug("Can't enter manufacturer mode: code=%d, size=%d",
381 ret,
382 transferred);
383 return (-1);
384 }
385
386 return (0);
387 }
388
389 int
iwmbt_exit_manufacturer(struct libusb_device_handle * hdl,int mode)390 iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode)
391 {
392 int ret, transferred;
393 static struct iwmbt_hci_cmd cmd = {
394 .opcode = htole16(0xfc11),
395 .length = 2,
396 .data = { 0x00, 0x00 },
397 };
398 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
399
400 /*
401 * The mode sets the type of reset we want to perform:
402 * 0x00: simply exit manufacturer mode without a reset.
403 * 0x01: exit manufacturer mode with a reset and patches disabled
404 * 0x02: exit manufacturer mode with a reset and patches enabled
405 */
406 if (mode > 2) {
407 iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)",
408 mode);
409 }
410 cmd.data[1] = mode;
411
412 ret = iwmbt_hci_command(hdl,
413 &cmd,
414 buf,
415 sizeof(buf),
416 &transferred,
417 IWMBT_HCI_CMD_TIMEOUT);
418
419 if (ret < 0) {
420 iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d",
421 ret,
422 transferred);
423 return (-1);
424 }
425
426 return (0);
427 }
428
429 int
iwmbt_get_version(struct libusb_device_handle * hdl,struct iwmbt_version * version)430 iwmbt_get_version(struct libusb_device_handle *hdl,
431 struct iwmbt_version *version)
432 {
433 int ret, transferred;
434 struct iwmbt_hci_event_cmd_compl*event;
435 struct iwmbt_hci_cmd cmd = {
436 .opcode = htole16(0xfc05),
437 .length = 0,
438 };
439 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
440
441 memset(buf, 0, sizeof(buf));
442
443 ret = iwmbt_hci_command(hdl,
444 &cmd,
445 buf,
446 sizeof(buf),
447 &transferred,
448 IWMBT_HCI_CMD_TIMEOUT);
449
450 if (ret < 0 || transferred != sizeof(buf)) {
451 iwmbt_debug("Can't get version: : code=%d, size=%d",
452 ret,
453 transferred);
454 return (-1);
455 }
456
457 event = (struct iwmbt_hci_event_cmd_compl *)buf;
458 memcpy(version, event->data, sizeof(struct iwmbt_version));
459
460 return (0);
461 }
462
463 int
iwmbt_get_boot_params(struct libusb_device_handle * hdl,struct iwmbt_boot_params * params)464 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
465 struct iwmbt_boot_params *params)
466 {
467 int ret, transferred = 0;
468 struct iwmbt_hci_event_cmd_compl *event;
469 struct iwmbt_hci_cmd cmd = {
470 .opcode = htole16(0xfc0d),
471 .length = 0,
472 };
473 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
474
475 memset(buf, 0, sizeof(buf));
476
477 ret = iwmbt_hci_command(hdl,
478 &cmd,
479 buf,
480 sizeof(buf),
481 &transferred,
482 IWMBT_HCI_CMD_TIMEOUT);
483
484 if (ret < 0 || transferred != sizeof(buf)) {
485 iwmbt_debug("Can't get boot params: code=%d, size=%d",
486 ret,
487 transferred);
488 return (-1);
489 }
490
491 event = (struct iwmbt_hci_event_cmd_compl *)buf;
492 memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
493
494 return (0);
495 }
496
497 int
iwmbt_intel_reset(struct libusb_device_handle * hdl,uint32_t boot_param)498 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
499 {
500 int ret, transferred = 0;
501 struct iwmbt_hci_event *event;
502 static struct iwmbt_hci_cmd cmd = {
503 .opcode = htole16(0xfc01),
504 .length = 8,
505 .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
506 };
507 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
508
509 le32enc(cmd.data + 4, boot_param);
510 memset(buf, 0, sizeof(buf));
511
512 ret = iwmbt_hci_command(hdl,
513 &cmd,
514 buf,
515 sizeof(buf),
516 &transferred,
517 IWMBT_HCI_CMD_TIMEOUT);
518
519 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
520 iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
521 ret,
522 transferred);
523 return (ret);
524 }
525
526 /* expect Vendor Specific Event 0x02 */
527 event = (struct iwmbt_hci_event *)buf;
528 if (event->header.event != 0xFF || event->data[0] != 0x02) {
529 iwmbt_err("Intel Reset completion event missed");
530 return (-1);
531 }
532
533 return (0);
534 }
535
536 int
iwmbt_load_ddc(struct libusb_device_handle * hdl,const struct iwmbt_firmware * ddc)537 iwmbt_load_ddc(struct libusb_device_handle *hdl,
538 const struct iwmbt_firmware *ddc)
539 {
540 int size, sent = 0;
541 int ret, transferred;
542 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
543 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
544
545 size = ddc->len;
546
547 iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
548
549 while (size > 0) {
550
551 memset(buf, 0, sizeof(buf));
552 cmd->opcode = htole16(0xfc8b);
553 cmd->length = ddc->buf[sent] + 1;
554 memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
555
556 iwmbt_debug("transferring %d bytes, offset %d",
557 cmd->length,
558 sent);
559
560 size -= cmd->length;
561 sent += cmd->length;
562
563 ret = iwmbt_hci_command(hdl,
564 cmd,
565 buf,
566 sizeof(buf),
567 &transferred,
568 IWMBT_HCI_CMD_TIMEOUT);
569
570 if (ret < 0) {
571 iwmbt_debug("Intel Write DDC failed: code=%d", ret);
572 return (-1);
573 }
574 }
575
576 return (0);
577 }
578
579 int
iwmbt_set_event_mask(struct libusb_device_handle * hdl)580 iwmbt_set_event_mask(struct libusb_device_handle *hdl)
581 {
582 int ret, transferred = 0;
583 static struct iwmbt_hci_cmd cmd = {
584 .opcode = htole16(0xfc52),
585 .length = 8,
586 .data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
587 };
588 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
589
590 ret = iwmbt_hci_command(hdl,
591 &cmd,
592 buf,
593 sizeof(buf),
594 &transferred,
595 IWMBT_HCI_CMD_TIMEOUT);
596
597 if (ret < 0)
598 iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);
599
600 return (ret);
601 }
602