Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/ath/carl9170/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 10 kB image not shown  

Quelle  fw.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Atheros CARL9170 driver
 *
 * firmware parser
 *
 * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
 */


#include <linux/kernel.h>
#include <linux/firmware.h>
#include <linux/crc32.h>
#include <linux/module.h>
#include "carl9170.h"
#include "fwcmd.h"
#include "version.h"

static const u8 otus_magic[4] __nonstring = { OTUS_MAGIC };

static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4],
 const unsigned int len, const u8 compatible_revision)
{
 const struct carl9170fw_desc_head *iter;

 carl9170fw_for_each_hdr(iter, ar->fw.desc) {
  if (carl9170fw_desc_cmp(iter, descid, len,
     compatible_revision))
   return (void *)iter;
 }

 /* needed to find the LAST desc */
 if (carl9170fw_desc_cmp(iter, descid, len,
    compatible_revision))
  return (void *)iter;

 return NULL;
}

static int carl9170_fw_verify_descs(struct ar9170 *ar,
 const struct carl9170fw_desc_head *head, unsigned int max_len)
{
 const struct carl9170fw_desc_head *pos;
 unsigned long pos_addr, end_addr;
 unsigned int pos_length;

 if (max_len < sizeof(*pos))
  return -ENODATA;

 max_len = min_t(unsigned int, CARL9170FW_DESC_MAX_LENGTH, max_len);

 pos = head;
 pos_addr = (unsigned long) pos;
 end_addr = pos_addr + max_len;

 while (pos_addr < end_addr) {
  if (pos_addr + sizeof(*head) > end_addr)
   return -E2BIG;

  pos_length = le16_to_cpu(pos->length);

  if (pos_length < sizeof(*head))
   return -EBADMSG;

  if (pos_length > max_len)
   return -EOVERFLOW;

  if (pos_addr + pos_length > end_addr)
   return -EMSGSIZE;

  if (carl9170fw_desc_cmp(pos, LAST_MAGIC,
     CARL9170FW_LAST_DESC_SIZE,
     CARL9170FW_LAST_DESC_CUR_VER))
   return 0;

  pos_addr += pos_length;
  pos = (void *)pos_addr;
  max_len -= pos_length;
 }
 return -EINVAL;
}

static void carl9170_fw_info(struct ar9170 *ar)
{
 const struct carl9170fw_motd_desc *motd_desc;
 unsigned int str_ver_len;
 u32 fw_date;

 dev_info(&ar->udev->dev, "driver API: %s 2%03d-%02d-%02d [%d-%d]\n",
  CARL9170FW_VERSION_GIT, CARL9170FW_VERSION_YEAR,
  CARL9170FW_VERSION_MONTH, CARL9170FW_VERSION_DAY,
  CARL9170FW_API_MIN_VER, CARL9170FW_API_MAX_VER);

 motd_desc = carl9170_fw_find_desc(ar, MOTD_MAGIC,
  sizeof(*motd_desc), CARL9170FW_MOTD_DESC_CUR_VER);

 if (motd_desc) {
  str_ver_len = strnlen(motd_desc->release,
   CARL9170FW_MOTD_RELEASE_LEN);

  fw_date = le32_to_cpu(motd_desc->fw_year_month_day);

  dev_info(&ar->udev->dev, "firmware API: %.*s 2%03d-%02d-%02d\n",
    str_ver_len, motd_desc->release,
    CARL9170FW_GET_YEAR(fw_date),
    CARL9170FW_GET_MONTH(fw_date),
    CARL9170FW_GET_DAY(fw_date));

  strscpy(ar->hw->wiphy->fw_version, motd_desc->release,
   sizeof(ar->hw->wiphy->fw_version));
 }
}

static bool valid_dma_addr(const u32 address)
{
 if (address >= AR9170_SRAM_OFFSET &&
     address < (AR9170_SRAM_OFFSET + AR9170_SRAM_SIZE))
  return true;

 return false;
}

static bool valid_cpu_addr(const u32 address)
{
 if (valid_dma_addr(address) || (address >= AR9170_PRAM_OFFSET &&
     address < (AR9170_PRAM_OFFSET + AR9170_PRAM_SIZE)))
  return true;

 return false;
}

static int carl9170_fw_checksum(struct ar9170 *ar, const __u8 *data,
    size_t len)
{
 const struct carl9170fw_otus_desc *otus_desc;
 const struct carl9170fw_last_desc *last_desc;
 const struct carl9170fw_chk_desc *chk_desc;
 unsigned long fin, diff;
 unsigned int dsc_len;
 u32 crc32;

 last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC,
  sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER);
 if (!last_desc)
  return -EINVAL;

 otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
  sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
 if (!otus_desc) {
  dev_err(&ar->udev->dev, "failed to find compatible firmware "
   "descriptor.\n");
  return -ENODATA;
 }

 chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC,
  sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER);

 if (!chk_desc) {
  dev_warn(&ar->udev->dev, "Unprotected firmware image.\n");
  return 0;
 }

 dsc_len = min_t(unsigned int, len,
   (unsigned long)chk_desc - (unsigned long)otus_desc);

 fin = (unsigned long) last_desc + sizeof(*last_desc);
 diff = fin - (unsigned long) otus_desc;

 if (diff < len)
  len -= diff;

 if (len < 256)
  return -EIO;

 crc32 = crc32_le(~0, data, len);
 if (cpu_to_le32(crc32) != chk_desc->fw_crc32) {
  dev_err(&ar->udev->dev, "fw checksum test failed.\n");
  return -ENOEXEC;
 }

 crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len);
 if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) {
  dev_err(&ar->udev->dev, "descriptor check failed.\n");
  return -EINVAL;
 }
 return 0;
}

static int carl9170_fw_tx_sequence(struct ar9170 *ar)
{
 const struct carl9170fw_txsq_desc *txsq_desc;

 txsq_desc = carl9170_fw_find_desc(ar, TXSQ_MAGIC, sizeof(*txsq_desc),
       CARL9170FW_TXSQ_DESC_CUR_VER);
 if (txsq_desc) {
  ar->fw.tx_seq_table = le32_to_cpu(txsq_desc->seq_table_addr);
  if (!valid_cpu_addr(ar->fw.tx_seq_table))
   return -EINVAL;
 } else {
  ar->fw.tx_seq_table = 0;
 }

 return 0;
}

static void carl9170_fw_set_if_combinations(struct ar9170 *ar,
         u16 if_comb_types)
{
 if (ar->fw.vif_num < 2)
  return;

 ar->if_comb_limits[0].max = ar->fw.vif_num;
 ar->if_comb_limits[0].types = if_comb_types;

 ar->if_combs[0].num_different_channels = 1;
 ar->if_combs[0].max_interfaces = ar->fw.vif_num;
 ar->if_combs[0].limits = ar->if_comb_limits;
 ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits);

 ar->hw->wiphy->iface_combinations = ar->if_combs;
 ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs);
}

static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
{
 const struct carl9170fw_otus_desc *otus_desc;
 int err;
 u16 if_comb_types;

 err = carl9170_fw_checksum(ar, data, len);
 if (err)
  return err;

 otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
  sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
 if (!otus_desc) {
  return -ENODATA;
 }

#define SUPP(feat)      \
 (carl9170fw_supports(otus_desc->feature_set, feat))

 if (!SUPP(CARL9170FW_DUMMY_FEATURE)) {
  dev_err(&ar->udev->dev, "invalid firmware descriptor "
   "format detected.\n");
  return -EINVAL;
 }

 ar->fw.api_version = otus_desc->api_ver;

 if (ar->fw.api_version < CARL9170FW_API_MIN_VER ||
     ar->fw.api_version > CARL9170FW_API_MAX_VER) {
  dev_err(&ar->udev->dev, "unsupported firmware api version.\n");
  return -EINVAL;
 }

 if (!SUPP(CARL9170FW_COMMAND_PHY) || SUPP(CARL9170FW_UNUSABLE) ||
     !SUPP(CARL9170FW_HANDLE_BACK_REQ)) {
  dev_err(&ar->udev->dev, "firmware does support "
   "mandatory features.\n");
  return -ECANCELED;
 }

 if (ilog2(le32_to_cpu(otus_desc->feature_set)) >=
  __CARL9170FW_FEATURE_NUM) {
  dev_warn(&ar->udev->dev, "driver does not support all "
    "firmware features.\n");
 }

 if (!SUPP(CARL9170FW_COMMAND_CAM)) {
  dev_info(&ar->udev->dev, "crypto offloading is disabled "
    "by firmware.\n");
  ar->fw.disable_offload_fw = true;
 }

 if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM))
  ieee80211_hw_set(ar->hw, SUPPORTS_PS);

 if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) {
  dev_err(&ar->udev->dev, "firmware does not provide "
   "mandatory interfaces.\n");
  return -EINVAL;
 }

 if (SUPP(CARL9170FW_MINIBOOT))
  ar->fw.offset = le16_to_cpu(otus_desc->miniboot_size);
 else
  ar->fw.offset = 0;

 if (SUPP(CARL9170FW_USB_DOWN_STREAM)) {
  ar->hw->extra_tx_headroom += sizeof(struct ar9170_stream);
  ar->fw.tx_stream = true;
 }

 if (SUPP(CARL9170FW_USB_UP_STREAM))
  ar->fw.rx_stream = true;

 if (SUPP(CARL9170FW_RX_FILTER)) {
  ar->fw.rx_filter = true;
  ar->rx_filter_caps = FIF_FCSFAIL | FIF_PLCPFAIL |
   FIF_CONTROL | FIF_PSPOLL | FIF_OTHER_BSS;
 }

 if (SUPP(CARL9170FW_HW_COUNTERS))
  ar->fw.hw_counters = true;

 if (SUPP(CARL9170FW_WOL))
  device_set_wakeup_enable(&ar->udev->dev, true);

 if (SUPP(CARL9170FW_RX_BA_FILTER))
  ar->fw.ba_filter = true;

 if_comb_types = BIT(NL80211_IFTYPE_STATION) |
   BIT(NL80211_IFTYPE_P2P_CLIENT);

 ar->fw.vif_num = otus_desc->vif_num;
 ar->fw.cmd_bufs = otus_desc->cmd_bufs;
 ar->fw.address = le32_to_cpu(otus_desc->fw_address);
 ar->fw.rx_size = le16_to_cpu(otus_desc->rx_max_frame_len);
 ar->fw.mem_blocks = min_t(unsigned int, otus_desc->tx_descs, 0xfe);
 atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks);
 ar->fw.mem_block_size = le16_to_cpu(otus_desc->tx_frag_len);

 if (ar->fw.vif_num >= AR9170_MAX_VIRTUAL_MAC || !ar->fw.vif_num ||
     ar->fw.mem_blocks < 16 || !ar->fw.cmd_bufs ||
     ar->fw.mem_block_size < 64 || ar->fw.mem_block_size > 512 ||
     ar->fw.rx_size > 32768 || ar->fw.rx_size < 4096 ||
     !valid_cpu_addr(ar->fw.address)) {
  dev_err(&ar->udev->dev, "firmware shows obvious signs of "
   "malicious tampering.\n");
  return -EINVAL;
 }

 ar->fw.beacon_addr = le32_to_cpu(otus_desc->bcn_addr);
 ar->fw.beacon_max_len = le16_to_cpu(otus_desc->bcn_len);

 if (valid_dma_addr(ar->fw.beacon_addr) && ar->fw.beacon_max_len >=
     AR9170_MAC_BCN_LENGTH_MAX) {
  ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);

  if (SUPP(CARL9170FW_WLANTX_CAB)) {
   if_comb_types |= BIT(NL80211_IFTYPE_AP);

#ifdef CONFIG_MAC80211_MESH
   if_comb_types |=
    BIT(NL80211_IFTYPE_MESH_POINT);
#endif /* CONFIG_MAC80211_MESH */
  }
 }

 carl9170_fw_set_if_combinations(ar, if_comb_types);

 ar->hw->wiphy->interface_modes |= if_comb_types;

 ar->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;

 /* As IBSS Encryption is software-based, IBSS RSN is supported. */
 ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
   WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_SUPPORTS_TDLS;

#undef SUPPORTED
 return carl9170_fw_tx_sequence(ar);
}

static struct carl9170fw_desc_head *
carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len)

{
 int scan = 0, found = 0;

 if (!carl9170fw_size_check(len)) {
  dev_err(&ar->udev->dev, "firmware size is out of bound.\n");
  return NULL;
 }

 while (scan < len - sizeof(struct carl9170fw_desc_head)) {
  if (fw_data[scan++] == otus_magic[found])
   found++;
  else
   found = 0;

  if (scan >= len)
   break;

  if (found == sizeof(otus_magic))
   break;
 }

 if (found != sizeof(otus_magic))
  return NULL;

 return (void *)&fw_data[scan - found];
}

int carl9170_parse_firmware(struct ar9170 *ar)
{
 const struct carl9170fw_desc_head *fw_desc = NULL;
 const struct firmware *fw = ar->fw.fw;
 unsigned long header_offset = 0;
 int err;

 if (WARN_ON(!fw))
  return -EINVAL;

 fw_desc = carl9170_find_fw_desc(ar, fw->data, fw->size);

 if (!fw_desc) {
  dev_err(&ar->udev->dev, "unsupported firmware.\n");
  return -ENODATA;
 }

 header_offset = (unsigned long)fw_desc - (unsigned long)fw->data;

 err = carl9170_fw_verify_descs(ar, fw_desc, fw->size - header_offset);
 if (err) {
  dev_err(&ar->udev->dev, "damaged firmware (%d).\n", err);
  return err;
 }

 ar->fw.desc = fw_desc;

 carl9170_fw_info(ar);

 err = carl9170_fw(ar, fw->data, fw->size);
 if (err) {
  dev_err(&ar->udev->dev, "failed to parse firmware (%d).\n",
   err);
  return err;
 }

 return 0;
}

Messung V0.5
C=98 H=99 G=98

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.