.. | .. |
---|
| 1 | +// SPDX-License-Identifier: ISC |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. |
---|
3 | | - * Copyright (c) 2018, The Linux Foundation. All rights reserved. |
---|
4 | | - * |
---|
5 | | - * Permission to use, copy, modify, and/or distribute this software for any |
---|
6 | | - * purpose with or without fee is hereby granted, provided that the above |
---|
7 | | - * copyright notice and this permission notice appear in all copies. |
---|
8 | | - * |
---|
9 | | - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
---|
10 | | - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
---|
11 | | - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
---|
12 | | - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
13 | | - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
---|
14 | | - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
---|
15 | | - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
| 4 | + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. |
---|
16 | 5 | */ |
---|
17 | 6 | |
---|
18 | 7 | /* Algorithmic part of the firmware download. |
---|
.. | .. |
---|
156 | 145 | size_t size) |
---|
157 | 146 | { |
---|
158 | 147 | const struct wil_fw_record_brd_file *rec = data; |
---|
| 148 | + u32 max_num_ent, i, ent_size; |
---|
159 | 149 | |
---|
160 | | - if (size < sizeof(*rec)) { |
---|
161 | | - wil_err_fw(wil, "brd_file record too short: %zu\n", size); |
---|
162 | | - return 0; |
---|
| 150 | + if (size <= offsetof(struct wil_fw_record_brd_file, brd_info)) { |
---|
| 151 | + wil_err(wil, "board record too short, size %zu\n", size); |
---|
| 152 | + return -EINVAL; |
---|
163 | 153 | } |
---|
164 | 154 | |
---|
165 | | - wil->brd_file_addr = le32_to_cpu(rec->base_addr); |
---|
166 | | - wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes); |
---|
| 155 | + ent_size = size - offsetof(struct wil_fw_record_brd_file, brd_info); |
---|
| 156 | + max_num_ent = ent_size / sizeof(struct brd_info); |
---|
167 | 157 | |
---|
168 | | - wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n", |
---|
169 | | - wil->brd_file_addr, wil->brd_file_max_size); |
---|
| 158 | + if (!max_num_ent) { |
---|
| 159 | + wil_err(wil, "brd info entries are missing\n"); |
---|
| 160 | + return -EINVAL; |
---|
| 161 | + } |
---|
| 162 | + |
---|
| 163 | + wil->brd_info = kcalloc(max_num_ent, sizeof(struct wil_brd_info), |
---|
| 164 | + GFP_KERNEL); |
---|
| 165 | + if (!wil->brd_info) |
---|
| 166 | + return -ENOMEM; |
---|
| 167 | + |
---|
| 168 | + for (i = 0; i < max_num_ent; i++) { |
---|
| 169 | + wil->brd_info[i].file_addr = |
---|
| 170 | + le32_to_cpu(rec->brd_info[i].base_addr); |
---|
| 171 | + wil->brd_info[i].file_max_size = |
---|
| 172 | + le32_to_cpu(rec->brd_info[i].max_size_bytes); |
---|
| 173 | + |
---|
| 174 | + if (!wil->brd_info[i].file_addr) |
---|
| 175 | + break; |
---|
| 176 | + |
---|
| 177 | + wil_dbg_fw(wil, |
---|
| 178 | + "brd info %d: file_addr 0x%x, file_max_size %d\n", |
---|
| 179 | + i, wil->brd_info[i].file_addr, |
---|
| 180 | + wil->brd_info[i].file_max_size); |
---|
| 181 | + } |
---|
| 182 | + |
---|
| 183 | + wil->num_of_brd_entries = i; |
---|
| 184 | + if (wil->num_of_brd_entries == 0) { |
---|
| 185 | + kfree(wil->brd_info); |
---|
| 186 | + wil->brd_info = NULL; |
---|
| 187 | + wil_dbg_fw(wil, |
---|
| 188 | + "no valid brd info entries, using brd file addr\n"); |
---|
| 189 | + |
---|
| 190 | + } else { |
---|
| 191 | + wil_dbg_fw(wil, "num of brd info entries %d\n", |
---|
| 192 | + wil->num_of_brd_entries); |
---|
| 193 | + } |
---|
170 | 194 | |
---|
171 | 195 | return 0; |
---|
172 | 196 | } |
---|
.. | .. |
---|
634 | 658 | } |
---|
635 | 659 | wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size); |
---|
636 | 660 | |
---|
| 661 | + /* re-initialize board info params */ |
---|
| 662 | + wil->num_of_brd_entries = 0; |
---|
| 663 | + kfree(wil->brd_info); |
---|
| 664 | + wil->brd_info = NULL; |
---|
| 665 | + |
---|
637 | 666 | for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) { |
---|
638 | 667 | rc1 = wil_fw_verify(wil, d, sz); |
---|
639 | 668 | if (rc1 < 0) { |
---|
.. | .. |
---|
647 | 676 | |
---|
648 | 677 | out: |
---|
649 | 678 | release_firmware(fw); |
---|
| 679 | + if (rc) |
---|
| 680 | + wil_err_fw(wil, "Loading <%s> failed, rc %d\n", name, rc); |
---|
650 | 681 | return rc; |
---|
651 | 682 | } |
---|
652 | 683 | |
---|
.. | .. |
---|
660 | 691 | { |
---|
661 | 692 | int rc = 0; |
---|
662 | 693 | const struct wil_fw_record_head *hdr = data; |
---|
663 | | - size_t s, hdr_sz; |
---|
| 694 | + size_t s, hdr_sz = 0; |
---|
664 | 695 | u16 type; |
---|
| 696 | + int i = 0; |
---|
665 | 697 | |
---|
666 | | - /* Assuming the board file includes only one header record and one data |
---|
667 | | - * record. Each record starts with wil_fw_record_head. |
---|
| 698 | + /* Assuming the board file includes only one file header |
---|
| 699 | + * and one or several data records. |
---|
| 700 | + * Each record starts with wil_fw_record_head. |
---|
668 | 701 | */ |
---|
669 | 702 | if (size < sizeof(*hdr)) |
---|
670 | 703 | return -EINVAL; |
---|
.. | .. |
---|
672 | 705 | if (s > size) |
---|
673 | 706 | return -EINVAL; |
---|
674 | 707 | |
---|
675 | | - /* Skip the header record and handle the data record */ |
---|
676 | | - hdr = (const void *)hdr + s; |
---|
| 708 | + /* Skip the header record and handle the data records */ |
---|
677 | 709 | size -= s; |
---|
678 | | - if (size < sizeof(*hdr)) |
---|
679 | | - return -EINVAL; |
---|
680 | | - hdr_sz = le32_to_cpu(hdr->size); |
---|
681 | 710 | |
---|
682 | | - if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size) |
---|
683 | | - return -EINVAL; |
---|
684 | | - if (sizeof(*hdr) + hdr_sz > size) |
---|
685 | | - return -EINVAL; |
---|
686 | | - if (hdr_sz % 4) { |
---|
687 | | - wil_err_fw(wil, "unaligned record size: %zu\n", |
---|
688 | | - hdr_sz); |
---|
689 | | - return -EINVAL; |
---|
| 711 | + for (hdr = data + s;; hdr = (const void *)hdr + s, size -= s, i++) { |
---|
| 712 | + if (size < sizeof(*hdr)) |
---|
| 713 | + break; |
---|
| 714 | + |
---|
| 715 | + if (i >= wil->num_of_brd_entries) { |
---|
| 716 | + wil_err_fw(wil, |
---|
| 717 | + "Too many brd records: %d, num of expected entries %d\n", |
---|
| 718 | + i, wil->num_of_brd_entries); |
---|
| 719 | + break; |
---|
| 720 | + } |
---|
| 721 | + |
---|
| 722 | + hdr_sz = le32_to_cpu(hdr->size); |
---|
| 723 | + s = sizeof(*hdr) + hdr_sz; |
---|
| 724 | + if (wil->brd_info[i].file_max_size && |
---|
| 725 | + hdr_sz > wil->brd_info[i].file_max_size) |
---|
| 726 | + return -EINVAL; |
---|
| 727 | + if (sizeof(*hdr) + hdr_sz > size) |
---|
| 728 | + return -EINVAL; |
---|
| 729 | + if (hdr_sz % 4) { |
---|
| 730 | + wil_err_fw(wil, "unaligned record size: %zu\n", |
---|
| 731 | + hdr_sz); |
---|
| 732 | + return -EINVAL; |
---|
| 733 | + } |
---|
| 734 | + type = le16_to_cpu(hdr->type); |
---|
| 735 | + if (type != wil_fw_type_data) { |
---|
| 736 | + wil_err_fw(wil, |
---|
| 737 | + "invalid record type for board file: %d\n", |
---|
| 738 | + type); |
---|
| 739 | + return -EINVAL; |
---|
| 740 | + } |
---|
| 741 | + if (hdr_sz < sizeof(struct wil_fw_record_data)) { |
---|
| 742 | + wil_err_fw(wil, "data record too short: %zu\n", hdr_sz); |
---|
| 743 | + return -EINVAL; |
---|
| 744 | + } |
---|
| 745 | + |
---|
| 746 | + wil_dbg_fw(wil, |
---|
| 747 | + "using info from fw file for record %d: addr[0x%08x], max size %d\n", |
---|
| 748 | + i, wil->brd_info[i].file_addr, |
---|
| 749 | + wil->brd_info[i].file_max_size); |
---|
| 750 | + |
---|
| 751 | + rc = __fw_handle_data(wil, &hdr[1], hdr_sz, |
---|
| 752 | + cpu_to_le32(wil->brd_info[i].file_addr)); |
---|
| 753 | + if (rc) |
---|
| 754 | + return rc; |
---|
690 | 755 | } |
---|
691 | | - type = le16_to_cpu(hdr->type); |
---|
692 | | - if (type != wil_fw_type_data) { |
---|
693 | | - wil_err_fw(wil, "invalid record type for board file: %d\n", |
---|
694 | | - type); |
---|
695 | | - return -EINVAL; |
---|
696 | | - } |
---|
697 | | - if (hdr_sz < sizeof(struct wil_fw_record_data)) { |
---|
698 | | - wil_err_fw(wil, "data record too short: %zu\n", hdr_sz); |
---|
| 756 | + |
---|
| 757 | + if (size) { |
---|
| 758 | + wil_err_fw(wil, "unprocessed bytes: %zu\n", size); |
---|
| 759 | + if (size >= sizeof(*hdr)) { |
---|
| 760 | + wil_err_fw(wil, |
---|
| 761 | + "Stop at offset %ld record type %d [%zd bytes]\n", |
---|
| 762 | + (long)((const void *)hdr - data), |
---|
| 763 | + le16_to_cpu(hdr->type), hdr_sz); |
---|
| 764 | + } |
---|
699 | 765 | return -EINVAL; |
---|
700 | 766 | } |
---|
701 | 767 | |
---|
702 | | - wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n", |
---|
703 | | - wil->brd_file_addr); |
---|
704 | | - |
---|
705 | | - rc = __fw_handle_data(wil, &hdr[1], hdr_sz, |
---|
706 | | - cpu_to_le32(wil->brd_file_addr)); |
---|
707 | | - |
---|
708 | | - return rc; |
---|
| 768 | + return 0; |
---|
709 | 769 | } |
---|
710 | 770 | |
---|
711 | 771 | /** |
---|
.. | .. |
---|
736 | 796 | rc = dlen; |
---|
737 | 797 | goto out; |
---|
738 | 798 | } |
---|
739 | | - /* Process the data record */ |
---|
| 799 | + |
---|
| 800 | + /* Process the data records */ |
---|
740 | 801 | rc = wil_brd_process(wil, brd->data, dlen); |
---|
741 | 802 | |
---|
742 | 803 | out: |
---|
743 | 804 | release_firmware(brd); |
---|
| 805 | + if (rc) |
---|
| 806 | + wil_err_fw(wil, "Loading <%s> failed, rc %d\n", name, rc); |
---|
744 | 807 | return rc; |
---|
745 | 808 | } |
---|
746 | 809 | |
---|