1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org> 5 * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> 6 * Copyright (c) 2023 Future Crew LLC. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/endian.h> 32 #include <sys/stat.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "rtlbt_fw.h" 43 #include "rtlbt_dbg.h" 44 45 static const struct rtlbt_id_table rtlbt_ic_id_table[] = { 46 { /* 8723A */ 47 .lmp_subversion = RTLBT_ROM_LMP_8723A, 48 .hci_revision = 0xb, 49 .hci_version = 0x6, 50 .flags = RTLBT_IC_FLAG_SIMPLE, 51 .fw_name = "rtl8723a", 52 }, { /* 8723B */ 53 .lmp_subversion = RTLBT_ROM_LMP_8723B, 54 .hci_revision = 0xb, 55 .hci_version = 0x6, 56 .fw_name = "rtl8723b", 57 }, { /* 8723D */ 58 .lmp_subversion = RTLBT_ROM_LMP_8723B, 59 .hci_revision = 0xd, 60 .hci_version = 0x8, 61 .flags = RTLBT_IC_FLAG_CONFIG, 62 .fw_name = "rtl8723d", 63 }, { /* 8821A */ 64 .lmp_subversion = RTLBT_ROM_LMP_8821A, 65 .hci_revision = 0xa, 66 .hci_version = 0x6, 67 .fw_name = "rtl8821a", 68 }, { /* 8821C */ 69 .lmp_subversion = RTLBT_ROM_LMP_8821A, 70 .hci_revision = 0xc, 71 .hci_version = 0x8, 72 .flags = RTLBT_IC_FLAG_MSFT, 73 .fw_name = "rtl8821c", 74 }, { /* 8761A */ 75 .lmp_subversion = RTLBT_ROM_LMP_8761A, 76 .hci_revision = 0xa, 77 .hci_version = 0x6, 78 .fw_name = "rtl8761a", 79 }, { /* 8761BU */ 80 .lmp_subversion = RTLBT_ROM_LMP_8761A, 81 .hci_revision = 0xb, 82 .hci_version = 0xa, 83 .fw_name = "rtl8761bu", 84 }, { /* 8822C with USB interface */ 85 .lmp_subversion = RTLBT_ROM_LMP_8822B, 86 .hci_revision = 0xc, 87 .hci_version = 0xa, 88 .flags = RTLBT_IC_FLAG_MSFT, 89 .fw_name = "rtl8822cu", 90 }, { /* 8822B */ 91 .lmp_subversion = RTLBT_ROM_LMP_8822B, 92 .hci_revision = 0xb, 93 .hci_version = 0x7, 94 .flags = RTLBT_IC_FLAG_CONFIG | RTLBT_IC_FLAG_MSFT, 95 .fw_name = "rtl8822b", 96 }, { /* 8852A */ 97 .lmp_subversion = RTLBT_ROM_LMP_8852A, 98 .hci_revision = 0xa, 99 .hci_version = 0xb, 100 .flags = RTLBT_IC_FLAG_MSFT, 101 .fw_name = "rtl8852au", 102 }, { /* 8852B */ 103 .lmp_subversion = RTLBT_ROM_LMP_8852A, 104 .hci_revision = 0xb, 105 .hci_version = 0xb, 106 .flags = RTLBT_IC_FLAG_MSFT, 107 .fw_name = "rtl8852bu", 108 }, { /* 8852C */ 109 .lmp_subversion = RTLBT_ROM_LMP_8852A, 110 .hci_revision = 0xc, 111 .hci_version = 0xc, 112 .flags = RTLBT_IC_FLAG_MSFT, 113 .fw_name = "rtl8852cu", 114 .fw_suffix = "_fw_v2.bin", 115 }, { /* 8851B */ 116 .lmp_subversion = RTLBT_ROM_LMP_8851B, 117 .hci_revision = 0xb, 118 .hci_version = 0xc, 119 .flags = RTLBT_IC_FLAG_MSFT, 120 .fw_name = "rtl8851bu", 121 }, { /* 8922A */ 122 .lmp_subversion = RTLBT_ROM_LMP_8922A, 123 .hci_revision = 0xa, 124 .hci_version = 0xc, 125 .flags = RTLBT_IC_FLAG_MSFT, 126 .fw_name = "rtl8922au", 127 }, { /* 8852BT/8852BE-VT */ 128 .lmp_subversion = RTLBT_ROM_LMP_8852A, 129 .hci_revision = 0x87, 130 .hci_version = 0xc, 131 .flags = RTLBT_IC_FLAG_MSFT, 132 .fw_name = "rtl8852btu", 133 }, 134 }; 135 136 static const uint16_t project_ids[] = { 137 [ 0 ] = RTLBT_ROM_LMP_8723A, 138 [ 1 ] = RTLBT_ROM_LMP_8723B, 139 [ 2 ] = RTLBT_ROM_LMP_8821A, 140 [ 3 ] = RTLBT_ROM_LMP_8761A, 141 [ 7 ] = RTLBT_ROM_LMP_8703B, 142 [ 8 ] = RTLBT_ROM_LMP_8822B, 143 [ 9 ] = RTLBT_ROM_LMP_8723B, /* 8723DU */ 144 [ 10 ] = RTLBT_ROM_LMP_8821A, /* 8821CU */ 145 [ 13 ] = RTLBT_ROM_LMP_8822B, /* 8822CU */ 146 [ 14 ] = RTLBT_ROM_LMP_8761A, /* 8761BU */ 147 [ 18 ] = RTLBT_ROM_LMP_8852A, /* 8852AU */ 148 [ 19 ] = RTLBT_ROM_LMP_8723B, /* 8723FU */ 149 [ 20 ] = RTLBT_ROM_LMP_8852A, /* 8852BU */ 150 [ 25 ] = RTLBT_ROM_LMP_8852A, /* 8852CU */ 151 [ 33 ] = RTLBT_ROM_LMP_8822B, /* 8822EU */ 152 [ 36 ] = RTLBT_ROM_LMP_8851B, /* 8851BU */ 153 [ 44 ] = RTLBT_ROM_LMP_8922A, /* 8922A */ 154 [ 47 ] = RTLBT_ROM_LMP_8852A, /* 8852BT */ 155 }; 156 157 /* Signatures */ 158 static const uint8_t fw_header_sig_v1[8] = 159 {0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68}; /* Realtech */ 160 static const uint8_t fw_header_sig_v2[8] = 161 {0x52, 0x54, 0x42, 0x54, 0x43, 0x6F, 0x72, 0x65}; /* RTBTCore */ 162 static const uint8_t fw_ext_sig[4] = {0x51, 0x04, 0xFD, 0x77}; 163 164 int 165 rtlbt_fw_read(struct rtlbt_firmware *fw, const char *fwname) 166 { 167 int fd; 168 struct stat sb; 169 unsigned char *buf; 170 ssize_t r; 171 172 fd = open(fwname, O_RDONLY); 173 if (fd < 0) { 174 warn("%s: open: %s", __func__, fwname); 175 return (0); 176 } 177 178 if (fstat(fd, &sb) != 0) { 179 warn("%s: stat: %s", __func__, fwname); 180 close(fd); 181 return (0); 182 } 183 184 buf = calloc(1, sb.st_size); 185 if (buf == NULL) { 186 warn("%s: calloc", __func__); 187 close(fd); 188 return (0); 189 } 190 191 /* XXX handle partial reads */ 192 r = read(fd, buf, sb.st_size); 193 if (r < 0) { 194 warn("%s: read", __func__); 195 free(buf); 196 close(fd); 197 return (0); 198 } 199 200 if (r != sb.st_size) { 201 rtlbt_err("read len %d != file size %d", 202 (int) r, 203 (int) sb.st_size); 204 free(buf); 205 close(fd); 206 return (0); 207 } 208 209 /* We have everything, so! */ 210 211 memset(fw, 0, sizeof(*fw)); 212 213 fw->fwname = strdup(fwname); 214 fw->len = sb.st_size; 215 fw->buf = buf; 216 217 close(fd); 218 return (1); 219 } 220 221 void 222 rtlbt_fw_free(struct rtlbt_firmware *fw) 223 { 224 if (fw->fwname) 225 free(fw->fwname); 226 if (fw->buf) 227 free(fw->buf); 228 memset(fw, 0, sizeof(*fw)); 229 } 230 231 char * 232 rtlbt_get_fwname(const char *fw_name, const char *prefix, const char *suffix) 233 { 234 char *fwname; 235 236 asprintf(&fwname, "%s/%s%s", prefix, fw_name, suffix); 237 238 return (fwname); 239 } 240 241 const struct rtlbt_id_table * 242 rtlbt_get_ic(uint16_t lmp_subversion, uint16_t hci_revision, 243 uint8_t hci_version) 244 { 245 unsigned int i; 246 247 for (i = 0; i < nitems(rtlbt_ic_id_table); i++) { 248 if (rtlbt_ic_id_table[i].lmp_subversion == lmp_subversion && 249 rtlbt_ic_id_table[i].hci_revision == hci_revision && 250 rtlbt_ic_id_table[i].hci_version == hci_version) 251 return (rtlbt_ic_id_table + i); 252 } 253 254 return (NULL); 255 } 256 257 enum rtlbt_fw_type 258 rtlbt_get_fw_type(struct rtlbt_firmware *fw, uint16_t *fw_lmp_subversion) 259 { 260 enum rtlbt_fw_type fw_type; 261 size_t fw_header_len; 262 uint8_t *ptr; 263 uint8_t opcode, oplen, project_id; 264 265 if (fw->len < 8) { 266 rtlbt_err("firmware file too small"); 267 return (RTLBT_FW_TYPE_UNKNOWN); 268 } 269 270 if (memcmp(fw->buf, fw_header_sig_v1, sizeof(fw_header_sig_v1)) == 0) { 271 fw_type = RTLBT_FW_TYPE_V1; 272 fw_header_len = sizeof(struct rtlbt_fw_header_v1); 273 } else 274 if (memcmp(fw->buf, fw_header_sig_v2, sizeof(fw_header_sig_v2)) == 0) { 275 fw_type = RTLBT_FW_TYPE_V2; 276 fw_header_len = sizeof(struct rtlbt_fw_header_v2); 277 } else 278 return (RTLBT_FW_TYPE_UNKNOWN); 279 280 if (fw->len < fw_header_len + sizeof(fw_ext_sig) + 4) { 281 rtlbt_err("firmware file too small"); 282 return (RTLBT_FW_TYPE_UNKNOWN); 283 } 284 285 ptr = fw->buf + fw->len - sizeof(fw_ext_sig); 286 if (memcmp(ptr, fw_ext_sig, sizeof(fw_ext_sig)) != 0) { 287 rtlbt_err("invalid extension section signature"); 288 return (RTLBT_FW_TYPE_UNKNOWN); 289 } 290 291 do { 292 opcode = *--ptr; 293 oplen = *--ptr; 294 ptr -= oplen; 295 296 rtlbt_debug("code=%x len=%x", opcode, oplen); 297 298 if (opcode == 0x00) { 299 if (oplen != 1) { 300 rtlbt_err("invalid instruction length"); 301 return (RTLBT_FW_TYPE_UNKNOWN); 302 } 303 project_id = *ptr; 304 rtlbt_debug("project_id=%x", project_id); 305 if (project_id >= nitems(project_ids) || 306 project_ids[project_id] == 0) { 307 rtlbt_err("unknown project id %x", project_id); 308 return (RTLBT_FW_TYPE_UNKNOWN); 309 } 310 *fw_lmp_subversion = project_ids[project_id]; 311 return (fw_type); 312 } 313 } while (opcode != 0xff && ptr > fw->buf + fw_header_len); 314 315 rtlbt_err("can not find project id instruction"); 316 return (RTLBT_FW_TYPE_UNKNOWN); 317 }; 318 319 int 320 rtlbt_parse_fwfile_v1(struct rtlbt_firmware *fw, uint8_t rom_version) 321 { 322 struct rtlbt_fw_header_v1 *fw_header; 323 uint8_t *patch_buf; 324 unsigned int i; 325 const uint8_t *chip_id_base; 326 uint32_t patch_offset; 327 uint16_t patch_length, num_patches; 328 329 fw_header = (struct rtlbt_fw_header_v1 *)fw->buf; 330 num_patches = le16toh(fw_header->num_patches); 331 rtlbt_debug("fw_version=%x, num_patches=%d", 332 le32toh(fw_header->fw_version), num_patches); 333 334 /* Find a right patch for the chip. */ 335 if (fw->len < sizeof(struct rtlbt_fw_header_v1) + 336 sizeof(fw_ext_sig) + 4 + 8 * num_patches) { 337 errno = EINVAL; 338 return (-1); 339 } 340 341 chip_id_base = fw->buf + sizeof(struct rtlbt_fw_header_v1); 342 for (i = 0; i < num_patches; i++) { 343 if (le16dec(chip_id_base + i * 2) != rom_version + 1) 344 continue; 345 patch_length = le16dec(chip_id_base + 2 * (num_patches + i)); 346 patch_offset = le32dec(chip_id_base + 4 * (num_patches + i)); 347 break; 348 } 349 350 if (i >= num_patches) { 351 rtlbt_err("can not find patch for chip id %d", rom_version); 352 errno = EINVAL; 353 return (-1); 354 } 355 356 rtlbt_debug( 357 "index=%d length=%x offset=%x", i, patch_length, patch_offset); 358 if (fw->len < patch_offset + patch_length) { 359 errno = EINVAL; 360 return (-1); 361 } 362 363 patch_buf = malloc(patch_length); 364 if (patch_buf == NULL) { 365 errno = ENOMEM; 366 return (-1); 367 } 368 369 memcpy(patch_buf, fw->buf + patch_offset, patch_length - 4); 370 memcpy(patch_buf + patch_length - 4, &fw_header->fw_version, 4); 371 372 free(fw->buf); 373 fw->buf = patch_buf; 374 fw->len = patch_length; 375 376 return (0); 377 } 378 379 static void * 380 rtlbt_iov_fetch(struct rtlbt_iov *iov, uint32_t len) 381 { 382 void *data = NULL; 383 384 if (iov->len >= len) { 385 data = iov->data; 386 iov->data += len; 387 iov->len -= len; 388 } 389 390 return (data); 391 } 392 393 static int 394 rtlbt_patch_entry_cmp(struct rtlbt_patch_entry *a, struct rtlbt_patch_entry *b, 395 void *thunk __unused) 396 { 397 return ((a->prio > b->prio) - (a->prio < b->prio)); 398 } 399 400 static int 401 rtlbt_parse_section(struct rtlbt_patch_list *patch_list, uint32_t opcode, 402 uint8_t *data, uint32_t len, uint8_t rom_version, uint8_t key_id) 403 { 404 struct rtlbt_sec_hdr *hdr; 405 struct rtlbt_patch_entry *entry; 406 struct rtlbt_subsec_hdr *subsec_hdr; 407 struct rtlbt_subsec_security_hdr *subsec_security_hdr; 408 uint16_t num_subsecs; 409 uint8_t *subsec_data; 410 uint32_t subsec_len; 411 int i, sec_len = 0; 412 struct rtlbt_iov iov = { 413 .data = data, 414 .len = len, 415 }; 416 417 hdr = rtlbt_iov_fetch(&iov, sizeof(*hdr)); 418 if (hdr == NULL) { 419 errno = EINVAL; 420 return (-1); 421 } 422 num_subsecs = le16toh(hdr->num); 423 424 for (i = 0; i < num_subsecs; i++) { 425 subsec_hdr = rtlbt_iov_fetch(&iov, sizeof(*subsec_hdr)); 426 if (subsec_hdr == NULL) 427 break; 428 subsec_len = le32toh(subsec_hdr->len); 429 430 rtlbt_debug("subsection, eco 0x%02x, prio 0x%02x, len 0x%x", 431 subsec_hdr->eco, subsec_hdr->prio, subsec_len); 432 433 subsec_data = rtlbt_iov_fetch(&iov, subsec_len); 434 if (subsec_data == NULL) 435 break; 436 437 if (subsec_hdr->eco == rom_version + 1) { 438 if (opcode == RTLBT_PATCH_SECURITY_HEADER) { 439 subsec_security_hdr = (void *)subsec_hdr; 440 if (subsec_security_hdr->key_id == key_id) 441 break; 442 continue; 443 } 444 445 entry = calloc(1, sizeof(*entry)); 446 if (entry == NULL) { 447 errno = ENOMEM; 448 return (-1); 449 } 450 *entry = (struct rtlbt_patch_entry) { 451 .opcode = opcode, 452 .len = subsec_len, 453 .prio = subsec_hdr->prio, 454 .data = subsec_data, 455 }; 456 SLIST_INSERT_HEAD(patch_list, entry, next); 457 sec_len += subsec_len; 458 } 459 } 460 461 return (sec_len); 462 } 463 464 int 465 rtlbt_parse_fwfile_v2(struct rtlbt_firmware *fw, uint8_t rom_version, 466 uint8_t key_id) 467 { 468 struct rtlbt_fw_header_v2 *hdr; 469 struct rtlbt_section *section; 470 struct rtlbt_patch_entry *entry; 471 uint32_t num_sections; 472 uint32_t section_len; 473 uint32_t opcode; 474 int seclen, len = 0, patch_len = 0; 475 uint32_t i; 476 uint8_t *section_data, *patch_buf; 477 struct rtlbt_patch_list patch_list = 478 SLIST_HEAD_INITIALIZER(patch_list); 479 struct rtlbt_iov iov = { 480 .data = fw->buf, 481 .len = fw->len - 7, 482 }; 483 484 hdr = rtlbt_iov_fetch(&iov, sizeof(*hdr)); 485 if (hdr == NULL) { 486 errno = EINVAL; 487 return (-1); 488 } 489 num_sections = le32toh(hdr->num_sections); 490 491 rtlbt_debug("FW version %02x%02x%02x%02x-%02x%02x%02x%02x", 492 hdr->fw_version[0], hdr->fw_version[1], 493 hdr->fw_version[2], hdr->fw_version[3], 494 hdr->fw_version[4], hdr->fw_version[5], 495 hdr->fw_version[6], hdr->fw_version[7]); 496 497 for (i = 0; i < num_sections; i++) { 498 section = rtlbt_iov_fetch(&iov, sizeof(*section)); 499 if (section == NULL) 500 break; 501 section_len = le32toh(section->len); 502 opcode = le32toh(section->opcode); 503 504 rtlbt_debug("section, opcode 0x%08x", section->opcode); 505 506 section_data = rtlbt_iov_fetch(&iov, section_len); 507 if (section_data == NULL) 508 break; 509 510 seclen = 0; 511 switch (opcode) { 512 case RTLBT_PATCH_SECURITY_HEADER: 513 if (key_id == 0) 514 break; 515 /* FALLTHROUGH */ 516 case RTLBT_PATCH_SNIPPETS: 517 case RTLBT_PATCH_DUMMY_HEADER: 518 seclen = rtlbt_parse_section(&patch_list, opcode, 519 section_data, section_len, rom_version, key_id); 520 break; 521 default: 522 break; 523 } 524 if (seclen < 0) { 525 rtlbt_err("Section parse (0x%08x) failed. err %d", 526 opcode, errno); 527 return (-1); 528 } 529 len += seclen; 530 } 531 532 if (len == 0) { 533 errno = ENOMSG; 534 return (-1); 535 } 536 537 patch_buf = calloc(1, len); 538 if (patch_buf == NULL) { 539 errno = ENOMEM; 540 return (-1); 541 } 542 543 SLIST_MERGESORT(&patch_list, NULL, 544 rtlbt_patch_entry_cmp, rtlbt_patch_entry, next); 545 while (!SLIST_EMPTY(&patch_list)) { 546 entry = SLIST_FIRST(&patch_list); 547 rtlbt_debug("opcode 0x%08x, addr 0x%p, len 0x%x", 548 entry->opcode, entry->data, entry->len); 549 memcpy(patch_buf + patch_len, entry->data, entry->len); 550 patch_len += entry->len; 551 SLIST_REMOVE_HEAD(&patch_list, next); 552 free(entry); 553 } 554 555 if (patch_len == 0) { 556 errno = EPERM; 557 return (-1); 558 } 559 560 free(fw->buf); 561 fw->buf = patch_buf; 562 fw->len = patch_len; 563 564 return (0); 565 } 566 567 int 568 rtlbt_append_fwfile(struct rtlbt_firmware *fw, struct rtlbt_firmware *opt) 569 { 570 uint8_t *buf; 571 int len; 572 573 len = fw->len + opt->len; 574 buf = realloc(fw->buf, len); 575 if (buf == NULL) 576 return (-1); 577 memcpy(buf + fw->len, opt->buf, opt->len); 578 fw->buf = buf; 579 fw->len = len; 580 581 return (0); 582 } 583