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 #ifdef RTLBTFW_SUPPORTS_FW_V2 109 }, { /* 8852C */ 110 .lmp_subversion = RTLBT_ROM_LMP_8852A, 111 .hci_revision = 0xc, 112 .hci_version = 0xc, 113 .flags = RTLBT_IC_FLAG_MSFT, 114 .fw_name = "rtl8852cu", 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 #endif 122 }, 123 }; 124 125 static const uint16_t project_ids[] = { 126 [ 0 ] = RTLBT_ROM_LMP_8723A, 127 [ 1 ] = RTLBT_ROM_LMP_8723B, 128 [ 2 ] = RTLBT_ROM_LMP_8821A, 129 [ 3 ] = RTLBT_ROM_LMP_8761A, 130 [ 7 ] = RTLBT_ROM_LMP_8703B, 131 [ 8 ] = RTLBT_ROM_LMP_8822B, 132 [ 9 ] = RTLBT_ROM_LMP_8723B, /* 8723DU */ 133 [ 10 ] = RTLBT_ROM_LMP_8821A, /* 8821CU */ 134 [ 13 ] = RTLBT_ROM_LMP_8822B, /* 8822CU */ 135 [ 14 ] = RTLBT_ROM_LMP_8761A, /* 8761BU */ 136 [ 18 ] = RTLBT_ROM_LMP_8852A, /* 8852AU */ 137 [ 19 ] = RTLBT_ROM_LMP_8723B, /* 8723FU */ 138 [ 20 ] = RTLBT_ROM_LMP_8852A, /* 8852BU */ 139 [ 25 ] = RTLBT_ROM_LMP_8852A, /* 8852CU */ 140 [ 33 ] = RTLBT_ROM_LMP_8822B, /* 8822EU */ 141 [ 36 ] = RTLBT_ROM_LMP_8851B, /* 8851BU */ 142 }; 143 144 /* Signatures */ 145 static const uint8_t fw_header_sig_v1[8] = 146 {0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68}; /* Realtech */ 147 #ifdef RTLBTFW_SUPPORTS_FW_V2 148 static const uint8_t fw_header_sig_v2[8] = 149 {0x52, 0x54, 0x42, 0x54, 0x43, 0x6F, 0x72, 0x65}; /* RTBTCore */ 150 #endif 151 static const uint8_t fw_ext_sig[4] = {0x51, 0x04, 0xFD, 0x77}; 152 153 int 154 rtlbt_fw_read(struct rtlbt_firmware *fw, const char *fwname) 155 { 156 int fd; 157 struct stat sb; 158 unsigned char *buf; 159 ssize_t r; 160 161 fd = open(fwname, O_RDONLY); 162 if (fd < 0) { 163 warn("%s: open: %s", __func__, fwname); 164 return (0); 165 } 166 167 if (fstat(fd, &sb) != 0) { 168 warn("%s: stat: %s", __func__, fwname); 169 close(fd); 170 return (0); 171 } 172 173 buf = calloc(1, sb.st_size); 174 if (buf == NULL) { 175 warn("%s: calloc", __func__); 176 close(fd); 177 return (0); 178 } 179 180 /* XXX handle partial reads */ 181 r = read(fd, buf, sb.st_size); 182 if (r < 0) { 183 warn("%s: read", __func__); 184 free(buf); 185 close(fd); 186 return (0); 187 } 188 189 if (r != sb.st_size) { 190 rtlbt_err("read len %d != file size %d", 191 (int) r, 192 (int) sb.st_size); 193 free(buf); 194 close(fd); 195 return (0); 196 } 197 198 /* We have everything, so! */ 199 200 memset(fw, 0, sizeof(*fw)); 201 202 fw->fwname = strdup(fwname); 203 fw->len = sb.st_size; 204 fw->buf = buf; 205 206 close(fd); 207 return (1); 208 } 209 210 void 211 rtlbt_fw_free(struct rtlbt_firmware *fw) 212 { 213 if (fw->fwname) 214 free(fw->fwname); 215 if (fw->buf) 216 free(fw->buf); 217 memset(fw, 0, sizeof(*fw)); 218 } 219 220 char * 221 rtlbt_get_fwname(const char *fw_name, const char *prefix, const char *suffix) 222 { 223 char *fwname; 224 225 asprintf(&fwname, "%s/%s%s", prefix, fw_name, suffix); 226 227 return (fwname); 228 } 229 230 const struct rtlbt_id_table * 231 rtlbt_get_ic(uint16_t lmp_subversion, uint16_t hci_revision, 232 uint8_t hci_version) 233 { 234 unsigned int i; 235 236 for (i = 0; i < nitems(rtlbt_ic_id_table); i++) { 237 if (rtlbt_ic_id_table[i].lmp_subversion == lmp_subversion && 238 rtlbt_ic_id_table[i].hci_revision == hci_revision && 239 rtlbt_ic_id_table[i].hci_version == hci_version) 240 return (rtlbt_ic_id_table + i); 241 } 242 243 return (NULL); 244 } 245 246 enum rtlbt_fw_type 247 rtlbt_get_fw_type(struct rtlbt_firmware *fw, uint16_t *fw_lmp_subversion) 248 { 249 enum rtlbt_fw_type fw_type; 250 size_t fw_header_len; 251 uint8_t *ptr; 252 uint8_t opcode, oplen, project_id; 253 254 if (fw->len < 8) { 255 rtlbt_err("firmware file too small"); 256 return (RTLBT_FW_TYPE_UNKNOWN); 257 } 258 259 if (memcmp(fw->buf, fw_header_sig_v1, sizeof(fw_header_sig_v1)) == 0) { 260 fw_type = RTLBT_FW_TYPE_V1; 261 fw_header_len = sizeof(struct rtlbt_fw_header_v1); 262 } else 263 #ifdef RTLBTFW_SUPPORTS_FW_V2 264 if (memcmp(fw->buf, fw_header_sig_v2, sizeof(fw_header_sig_v2)) == 0) { 265 fw_type = RTLBT_FW_TYPE_V2; 266 fw_header_len = sizeof(struct rtlbt_fw_header_v2); 267 } else 268 #endif 269 return (RTLBT_FW_TYPE_UNKNOWN); 270 271 if (fw->len < fw_header_len + sizeof(fw_ext_sig) + 4) { 272 rtlbt_err("firmware file too small"); 273 return (RTLBT_FW_TYPE_UNKNOWN); 274 } 275 276 ptr = fw->buf + fw->len - sizeof(fw_ext_sig); 277 if (memcmp(ptr, fw_ext_sig, sizeof(fw_ext_sig)) != 0) { 278 rtlbt_err("invalid extension section signature"); 279 return (RTLBT_FW_TYPE_UNKNOWN); 280 } 281 282 do { 283 opcode = *--ptr; 284 oplen = *--ptr; 285 ptr -= oplen; 286 287 rtlbt_debug("code=%x len=%x", opcode, oplen); 288 289 if (opcode == 0x00) { 290 if (oplen != 1) { 291 rtlbt_err("invalid instruction length"); 292 return (RTLBT_FW_TYPE_UNKNOWN); 293 } 294 project_id = *ptr; 295 rtlbt_debug("project_id=%x", project_id); 296 if (project_id >= nitems(project_ids) || 297 project_ids[project_id] == 0) { 298 rtlbt_err("unknown project id %x", project_id); 299 return (RTLBT_FW_TYPE_UNKNOWN); 300 } 301 *fw_lmp_subversion = project_ids[project_id]; 302 return (fw_type); 303 } 304 } while (opcode != 0xff && ptr > fw->buf + fw_header_len); 305 306 rtlbt_err("can not find project id instruction"); 307 return (RTLBT_FW_TYPE_UNKNOWN); 308 }; 309 310 int 311 rtlbt_parse_fwfile_v1(struct rtlbt_firmware *fw, uint8_t rom_version) 312 { 313 struct rtlbt_fw_header_v1 *fw_header; 314 uint8_t *patch_buf; 315 unsigned int i; 316 const uint8_t *chip_id_base; 317 uint32_t patch_offset; 318 uint16_t patch_length, num_patches; 319 320 fw_header = (struct rtlbt_fw_header_v1 *)fw->buf; 321 num_patches = le16toh(fw_header->num_patches); 322 rtlbt_debug("fw_version=%x, num_patches=%d", 323 le32toh(fw_header->fw_version), num_patches); 324 325 /* Find a right patch for the chip. */ 326 if (fw->len < sizeof(struct rtlbt_fw_header_v1) + 327 sizeof(fw_ext_sig) + 4 + 8 * num_patches) { 328 errno = EINVAL; 329 return (-1); 330 } 331 332 chip_id_base = fw->buf + sizeof(struct rtlbt_fw_header_v1); 333 for (i = 0; i < num_patches; i++) { 334 if (le16dec(chip_id_base + i * 2) != rom_version + 1) 335 continue; 336 patch_length = le16dec(chip_id_base + 2 * (num_patches + i)); 337 patch_offset = le32dec(chip_id_base + 4 * (num_patches + i)); 338 break; 339 } 340 341 if (i >= num_patches) { 342 rtlbt_err("can not find patch for chip id %d", rom_version); 343 errno = EINVAL; 344 return (-1); 345 } 346 347 rtlbt_debug( 348 "index=%d length=%x offset=%x", i, patch_length, patch_offset); 349 if (fw->len < patch_offset + patch_length) { 350 errno = EINVAL; 351 return (-1); 352 } 353 354 patch_buf = malloc(patch_length); 355 if (patch_buf == NULL) { 356 errno = ENOMEM; 357 return (-1); 358 } 359 360 memcpy(patch_buf, fw->buf + patch_offset, patch_length - 4); 361 memcpy(patch_buf + patch_length - 4, &fw_header->fw_version, 4); 362 363 free(fw->buf); 364 fw->buf = patch_buf; 365 fw->len = patch_length; 366 367 return (0); 368 } 369 370 int 371 rtlbt_append_fwfile(struct rtlbt_firmware *fw, struct rtlbt_firmware *opt) 372 { 373 uint8_t *buf; 374 int len; 375 376 len = fw->len + opt->len; 377 buf = realloc(fw->buf, len); 378 if (buf == NULL) 379 return (-1); 380 memcpy(buf + fw->len, opt->buf, opt->len); 381 fw->buf = buf; 382 fw->len = len; 383 384 return (0); 385 } 386