> Date: Mon, 28 Oct 2019 19:28:29 +0100
> From: Stefan Sperling <[email protected]>
> 
> Newer iwm(4) firmware versions will require paging to host DRAM.
> This diff implements support for this. It was written by Imre Vadasz in 2017.
> 
> I would like to get review by people with experience in the kernel memory
> subsystem and DMA, to check that this diff isn't doing something stupid.

I have trouble understanding what the bus_dmamap_sync() calls in
iwm_alloc_fw_paging_mem() are supposed to achieve.

> As it is, the diff can only be tested for regressions because our current
> firmware versions do not use paging. And the diff alone is not sufficient to
> make newer firmware versions work with our driver, just one step on the way.
> The diff seems to work OK with -31 and -34 8265 firmware in combination with
> additional patches I have in my tree.
> It applies on top of -current which just received a couple of commits to iwm.
> Make sure your tree is up-to-date before applying it.
> 
> diff b8720deb3d8c2ae7a0a629e71606d48da4c53669 
> 6bfc87e6a1a13d601330e23b3cee057701e0a418
> blob - a14efb48f10b28d578331ce295178abdf58862d7
> blob + 8b4ab3a2132ed1e4947151a309a9e78b90e31009
> --- sys/dev/pci/if_iwm.c
> +++ sys/dev/pci/if_iwm.c
> @@ -457,6 +457,9 @@ int       iwm_sf_config(struct iwm_softc *, int);
>  int  iwm_send_bt_init_conf(struct iwm_softc *);
>  int  iwm_send_update_mcc_cmd(struct iwm_softc *, const char *);
>  void iwm_tt_tx_backoff(struct iwm_softc *, uint32_t);
> +void iwm_free_fw_paging(struct iwm_softc *);
> +int  iwm_save_fw_paging(struct iwm_softc *, const struct iwm_fw_sects *);
> +int  iwm_send_paging_cmd(struct iwm_softc *, const struct iwm_fw_sects *);
>  int  iwm_init_hw(struct iwm_softc *);
>  int  iwm_init(struct ifnet *);
>  void iwm_start(struct ifnet *);
> @@ -584,6 +587,8 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode
>       struct iwm_ucode_tlv tlv;
>       uint32_t tlv_type;
>       uint8_t *data;
> +     uint32_t usniffer_img;
> +     uint32_t paging_mem_size;
>       int err;
>       size_t len;
>  
> @@ -787,6 +792,37 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode
>                               goto parse_out;
>                       break;
>  
> +             case IWM_UCODE_TLV_PAGING:
> +                     if (tlv_len != sizeof(uint32_t)) {
> +                             err = EINVAL;
> +                             goto parse_out;
> +                     }
> +                     paging_mem_size = le32toh(*(const uint32_t *)tlv_data);
> +
> +                     DPRINTF(("%s: Paging: paging enabled (size = %u 
> bytes)\n",
> +                         DEVNAME(sc), paging_mem_size));
> +                     if (paging_mem_size > IWM_MAX_PAGING_IMAGE_SIZE) {
> +                             printf("%s: Driver only supports up to %u"
> +                                 " bytes for paging image (%u requested)\n",
> +                                 DEVNAME(sc), IWM_MAX_PAGING_IMAGE_SIZE,
> +                                 paging_mem_size);
> +                             err = EINVAL;
> +                             goto out;
> +                     }
> +                     if (paging_mem_size & (IWM_FW_PAGING_SIZE - 1)) {
> +                             printf("%s: Paging: image isn't multiple of 
> %u\n",
> +                                 DEVNAME(sc), IWM_FW_PAGING_SIZE);
> +                             err = EINVAL;
> +                             goto out;
> +                     }
> +
> +                     fw->fw_sects[IWM_UCODE_TYPE_REGULAR].paging_mem_size =
> +                         paging_mem_size;
> +                     usniffer_img = IWM_UCODE_TYPE_REGULAR_USNIFFER;
> +                     fw->fw_sects[usniffer_img].paging_mem_size =
> +                         paging_mem_size;
> +                     break;
> +
>               case IWM_UCODE_TLV_N_SCAN_CHANNELS:
>                       if (tlv_len != sizeof(uint32_t)) {
>                               err = EINVAL;
> @@ -3219,6 +3255,7 @@ iwm_load_ucode_wait_alive(struct iwm_softc *sc,
>       enum iwm_ucode_type ucode_type)
>  {
>       enum iwm_ucode_type old_type = sc->sc_uc_current;
> +     struct iwm_fw_sects *fw = &sc->sc_fw.fw_sects[ucode_type];
>       int err;
>  
>       err = iwm_read_firmware(sc, ucode_type);
> @@ -3237,7 +3274,33 @@ iwm_load_ucode_wait_alive(struct iwm_softc *sc,
>               return err;
>       }
>  
> -     return iwm_post_alive(sc);
> +     err = iwm_post_alive(sc);
> +     if (err)
> +             return err;
> +
> +     /*
> +      * configure and operate fw paging mechanism.
> +      * driver configures the paging flow only once, CPU2 paging image
> +      * included in the IWM_UCODE_INIT image.
> +      */
> +     if (fw->paging_mem_size) {
> +             err = iwm_save_fw_paging(sc, fw);
> +             if (err) {
> +                     printf("%s: failed to save the FW paging image\n",
> +                         DEVNAME(sc));
> +                     return err;
> +             }
> +
> +             err = iwm_send_paging_cmd(sc, fw);
> +             if (err) {
> +                     printf("%s: failed to send the paging cmd\n",
> +                         DEVNAME(sc));
> +                     iwm_free_fw_paging(sc);
> +                     return err;
> +             }
> +     }
> +
> +     return 0;
>  }
>  
>  int
> @@ -6348,7 +6411,228 @@ iwm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backo
>       iwm_send_cmd(sc, &cmd);
>  }
>  
> +void
> +iwm_free_fw_paging(struct iwm_softc *sc)
> +{
> +     int i;
> +
> +     if (sc->fw_paging_db[0].fw_paging_block.vaddr == NULL)
> +             return;
> +
> +     for (i = 0; i < IWM_NUM_OF_FW_PAGING_BLOCKS; i++) {
> +             iwm_dma_contig_free(&sc->fw_paging_db[i].fw_paging_block);
> +     }
> +
> +     memset(sc->fw_paging_db, 0, sizeof(sc->fw_paging_db));
> +}
> +
>  int
> +iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image)
> +{
> +     int sec_idx, idx;
> +     uint32_t offset = 0;
> +
> +     /*
> +      * find where is the paging image start point:
> +      * if CPU2 exist and it's in paging format, then the image looks like:
> +      * CPU1 sections (2 or more)
> +      * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2
> +      * CPU2 sections (not paged)
> +      * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2
> +      * non paged to CPU2 paging sec
> +      * CPU2 paging CSS
> +      * CPU2 paging image (including instruction and data)
> +      */
> +     for (sec_idx = 0; sec_idx < IWM_UCODE_SECT_MAX; sec_idx++) {
> +             if (image->fw_sect[sec_idx].fws_devoff ==
> +                 IWM_PAGING_SEPARATOR_SECTION) {
> +                     sec_idx++;
> +                     break;
> +             }
> +     }
> +
> +     /*
> +      * If paging is enabled there should be at least 2 more sections left
> +      * (one for CSS and one for Paging data)
> +      */
> +     if (sec_idx >= nitems(image->fw_sect) - 1) {
> +             printf("%s: Paging: Missing CSS and/or paging sections\n",
> +                 DEVNAME(sc));
> +             iwm_free_fw_paging(sc);
> +             return EINVAL;
> +     }
> +
> +     /* copy the CSS block to the dram */
> +     DPRINTF(("%s: Paging: load paging CSS to FW, sec = %d\n",
> +         DEVNAME(sc), sec_idx));
> +
> +     memcpy(sc->fw_paging_db[0].fw_paging_block.vaddr,
> +         image->fw_sect[sec_idx].fws_data,
> +         sc->fw_paging_db[0].fw_paging_size);
> +
> +     DPRINTF(("%s: Paging: copied %d CSS bytes to first block\n",
> +         DEVNAME(sc), sc->fw_paging_db[0].fw_paging_size));
> +
> +     sec_idx++;
> +
> +     /*
> +      * copy the paging blocks to the dram
> +      * loop index start from 1 since that CSS block already copied to dram
> +      * and CSS index is 0.
> +      * loop stop at num_of_paging_blk since that last block is not full.
> +      */
> +     for (idx = 1; idx < sc->num_of_paging_blk; idx++) {
> +             memcpy(sc->fw_paging_db[idx].fw_paging_block.vaddr,
> +                 (const char *)image->fw_sect[sec_idx].fws_data + offset,
> +                 sc->fw_paging_db[idx].fw_paging_size);
> +
> +             DPRINTF(("%s: Paging: copied %d paging bytes to block %d\n",
> +                 DEVNAME(sc), sc->fw_paging_db[idx].fw_paging_size, idx));
> +
> +             offset += sc->fw_paging_db[idx].fw_paging_size;
> +     }
> +
> +     /* copy the last paging block */
> +     if (sc->num_of_pages_in_last_blk > 0) {
> +             memcpy(sc->fw_paging_db[idx].fw_paging_block.vaddr,
> +                 (const char *)image->fw_sect[sec_idx].fws_data + offset,
> +                 IWM_FW_PAGING_SIZE * sc->num_of_pages_in_last_blk);
> +
> +             DPRINTF(("%s: Paging: copied %d pages in the last block %d\n",
> +                 DEVNAME(sc), sc->num_of_pages_in_last_blk, idx));
> +     }
> +
> +     return 0;
> +}
> +
> +int
> +iwm_alloc_fw_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects 
> *image)
> +{
> +     int blk_idx = 0;
> +     int error, num_of_pages;
> +
> +     if (sc->fw_paging_db[0].fw_paging_block.vaddr != NULL) {
> +             int i;
> +             /* Device got reset, and we setup firmware paging again */
> +             bus_dmamap_sync(sc->sc_dmat,
> +                 sc->fw_paging_db[0].fw_paging_block.map,
> +                 0, IWM_FW_PAGING_SIZE,
> +                 BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
> +             for (i = 1; i < sc->num_of_paging_blk + 1; i++) {
> +                     bus_dmamap_sync(sc->sc_dmat,
> +                         sc->fw_paging_db[i].fw_paging_block.map,
> +                         0, IWM_PAGING_BLOCK_SIZE,
> +                         BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
> +             }
> +             return 0;
> +     }
> +
> +     /* ensure IWM_BLOCK_2_EXP_SIZE is power of 2 of IWM_PAGING_BLOCK_SIZE */
> +#if (1 << IWM_BLOCK_2_EXP_SIZE) != IWM_PAGING_BLOCK_SIZE
> +#error IWM_BLOCK_2_EXP_SIZE must be power of 2 of IWM_PAGING_BLOCK_SIZE
> +#endif
> +
> +     num_of_pages = image->paging_mem_size / IWM_FW_PAGING_SIZE;
> +     sc->num_of_paging_blk =
> +         ((num_of_pages - 1) / IWM_NUM_OF_PAGE_PER_GROUP) + 1;
> +
> +     sc->num_of_pages_in_last_blk =
> +             num_of_pages -
> +             IWM_NUM_OF_PAGE_PER_GROUP * (sc->num_of_paging_blk - 1);
> +
> +     DPRINTF(("%s: Paging: allocating mem for %d paging blocks, each block"
> +         " holds 8 pages, last block holds %d pages\n", DEVNAME(sc),
> +         sc->num_of_paging_blk,
> +         sc->num_of_pages_in_last_blk));
> +
> +     /* allocate block of 4Kbytes for paging CSS */
> +     error = iwm_dma_contig_alloc(sc->sc_dmat,
> +         &sc->fw_paging_db[blk_idx].fw_paging_block, IWM_FW_PAGING_SIZE,
> +         4096);
> +     if (error) {
> +             /* free all the previous pages since we failed */
> +             iwm_free_fw_paging(sc);
> +             return ENOMEM;
> +     }
> +
> +     sc->fw_paging_db[blk_idx].fw_paging_size = IWM_FW_PAGING_SIZE;
> +
> +     DPRINTF(("%s: Paging: allocated 4K(CSS) bytes for firmware paging.\n",
> +         DEVNAME(sc)));
> +
> +     /*
> +      * allocate blocks in dram.
> +      * since that CSS allocated in fw_paging_db[0] loop start from index 1
> +      */
> +     for (blk_idx = 1; blk_idx < sc->num_of_paging_blk + 1; blk_idx++) {
> +             /* allocate block of IWM_PAGING_BLOCK_SIZE (32K) */
> +             /* XXX Use iwm_dma_contig_alloc for allocating */
> +             error = iwm_dma_contig_alloc(sc->sc_dmat,
> +                  &sc->fw_paging_db[blk_idx].fw_paging_block,
> +                 IWM_PAGING_BLOCK_SIZE, 4096);
> +             if (error) {
> +                     /* free all the previous pages since we failed */
> +                     iwm_free_fw_paging(sc);
> +                     return ENOMEM;
> +             }
> +
> +             sc->fw_paging_db[blk_idx].fw_paging_size =
> +                 IWM_PAGING_BLOCK_SIZE;
> +
> +             DPRINTF((
> +                 "%s: Paging: allocated 32K bytes for firmware paging.\n",
> +                 DEVNAME(sc)));
> +     }
> +
> +     return 0;
> +}
> +
> +int
> +iwm_save_fw_paging(struct iwm_softc *sc, const struct iwm_fw_sects *fw)
> +{
> +     int ret;
> +
> +     ret = iwm_alloc_fw_paging_mem(sc, fw);
> +     if (ret)
> +             return ret;
> +
> +     return iwm_fill_paging_mem(sc, fw);
> +}
> +
> +/* send paging cmd to FW in case CPU2 has paging image */
> +int
> +iwm_send_paging_cmd(struct iwm_softc *sc, const struct iwm_fw_sects *fw)
> +{
> +     int blk_idx;
> +     uint32_t dev_phy_addr;
> +     struct iwm_fw_paging_cmd fw_paging_cmd = {
> +             .flags =
> +                     htole32(IWM_PAGING_CMD_IS_SECURED |
> +                             IWM_PAGING_CMD_IS_ENABLED |
> +                             (sc->num_of_pages_in_last_blk <<
> +                             IWM_PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),
> +             .block_size = htole32(IWM_BLOCK_2_EXP_SIZE),
> +             .block_num = htole32(sc->num_of_paging_blk),
> +     };
> +
> +     /* loop for for all paging blocks + CSS block */
> +     for (blk_idx = 0; blk_idx < sc->num_of_paging_blk + 1; blk_idx++) {
> +             dev_phy_addr = htole32(
> +                 sc->fw_paging_db[blk_idx].fw_paging_block.paddr >>
> +                 IWM_PAGE_2_EXP_SIZE);
> +             fw_paging_cmd.device_phy_addr[blk_idx] = dev_phy_addr;
> +             bus_dmamap_sync(sc->sc_dmat,
> +                 sc->fw_paging_db[blk_idx].fw_paging_block.map, 0,
> +                 blk_idx == 0 ? IWM_FW_PAGING_SIZE : IWM_PAGING_BLOCK_SIZE,
> +                 BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
> +     }
> +
> +     return iwm_send_cmd_pdu(sc, iwm_cmd_id(IWM_FW_PAGING_BLOCK_CMD,
> +                                            IWM_LONG_GROUP, 0),
> +         0, sizeof(fw_paging_cmd), &fw_paging_cmd);
> +}
> +
> +int
>  iwm_init_hw(struct iwm_softc *sc)
>  {
>       struct ieee80211com *ic = &sc->sc_ic;
> @@ -7188,6 +7472,8 @@ iwm_notif_intr(struct iwm_softc *sc)
>               case IWM_REMOVE_STA:
>               case IWM_TXPATH_FLUSH:
>               case IWM_LQ_CMD:
> +             case IWM_WIDE_ID(IWM_LONG_GROUP,
> +                              IWM_FW_PAGING_BLOCK_CMD):
>               case IWM_BT_CONFIG:
>               case IWM_REPLY_THERMAL_MNG_BACKOFF:
>               case IWM_NVM_ACCESS_CMD:
> blob - 8919f30dc6b86e23fb9433fbe2998394c17eb7b6
> blob + 1e6308bbd5b4958427d4a354fb4f339688bbc8d1
> --- sys/dev/pci/if_iwmreg.h
> +++ sys/dev/pci/if_iwmreg.h
> @@ -1737,6 +1737,9 @@ struct iwm_agn_scd_bc_tbl {
>  #define IWM_CALIBRATION_COMPLETE_NOTIFICATION        0x67
>  #define IWM_RADIO_VERSION_NOTIFICATION               0x68
>  
> +/* paging block to FW cpu2 */
> +#define IWM_FW_PAGING_BLOCK_CMD      0x4f
> +
>  /* Scan offload */
>  #define IWM_SCAN_OFFLOAD_REQUEST_CMD         0x51
>  #define IWM_SCAN_OFFLOAD_ABORT_CMD           0x52
> @@ -2099,6 +2102,57 @@ struct iwm_nvm_access_cmd {
>       uint16_t length;
>       uint8_t data[];
>  } __packed; /* IWM_NVM_ACCESS_CMD_API_S_VER_2 */
> +
> +/*
> + * Block paging calculations
> + */
> +#define IWM_PAGE_2_EXP_SIZE 12 /* 4K == 2^12 */
> +#define IWM_FW_PAGING_SIZE (1 << IWM_PAGE_2_EXP_SIZE) /* page size is 4KB */
> +#define IWM_PAGE_PER_GROUP_2_EXP_SIZE 3
> +/* 8 pages per group */
> +#define IWM_NUM_OF_PAGE_PER_GROUP (1 << IWM_PAGE_PER_GROUP_2_EXP_SIZE)
> +/* don't change, support only 32KB size */
> +#define IWM_PAGING_BLOCK_SIZE (IWM_NUM_OF_PAGE_PER_GROUP * 
> IWM_FW_PAGING_SIZE)
> +/* 32K == 2^15 */
> +#define IWM_BLOCK_2_EXP_SIZE (IWM_PAGE_2_EXP_SIZE + 
> IWM_PAGE_PER_GROUP_2_EXP_SIZE)
> +
> +/*
> + * Image paging calculations
> + */
> +#define IWM_BLOCK_PER_IMAGE_2_EXP_SIZE 5
> +/* 2^5 == 32 blocks per image */
> +#define IWM_NUM_OF_BLOCK_PER_IMAGE (1 << IWM_BLOCK_PER_IMAGE_2_EXP_SIZE)
> +/* maximum image size 1024KB */
> +#define IWM_MAX_PAGING_IMAGE_SIZE (IWM_NUM_OF_BLOCK_PER_IMAGE * 
> IWM_PAGING_BLOCK_SIZE)
> +
> +/* Virtual address signature */
> +#define IWM_PAGING_ADDR_SIG 0xAA000000
> +
> +#define IWM_PAGING_CMD_IS_SECURED (1 << 9)
> +#define IWM_PAGING_CMD_IS_ENABLED (1 << 8)
> +#define IWM_PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS 0
> +#define IWM_PAGING_TLV_SECURE_MASK 1
> +
> +#define IWM_NUM_OF_FW_PAGING_BLOCKS  33 /* 32 for data and 1 block for CSS */
> +
> +/*
> + * struct iwm_fw_paging_cmd - paging layout
> + *
> + * (IWM_FW_PAGING_BLOCK_CMD = 0x4f)
> + *
> + * Send to FW the paging layout in the driver.
> + *
> + * @flags: various flags for the command
> + * @block_size: the block size in powers of 2
> + * @block_num: number of blocks specified in the command.
> + * @device_phy_addr: virtual addresses from device side
> +*/
> +struct iwm_fw_paging_cmd {
> +     uint32_t flags;
> +     uint32_t block_size;
> +     uint32_t block_num;
> +     uint32_t device_phy_addr[IWM_NUM_OF_FW_PAGING_BLOCKS];
> +} __packed; /* IWM_FW_PAGING_BLOCK_CMD_API_S_VER_1 */
>  
>  /**
>   * struct iwm_nvm_access_resp_ver2 - response to IWM_NVM_ACCESS_CMD
> blob - 0eaf07bed8dfc0ec674e55b3da211ef88ad93dfe
> blob + edf48df74c872bdeab3a08c65bf2434e4f297509
> --- sys/dev/pci/if_iwmvar.h
> +++ sys/dev/pci/if_iwmvar.h
> @@ -173,6 +173,7 @@ struct iwm_fw_info {
>               } fw_sect[IWM_UCODE_SECT_MAX];
>               size_t fw_totlen;
>               int fw_count;
> +             uint32_t paging_mem_size;
>       } fw_sects[IWM_UCODE_TYPE_MAX];
>  };
>  
> @@ -237,6 +238,16 @@ struct iwm_dma_info {
>       bus_size_t              size;
>  };
>  
> +/**
> + * struct iwm_fw_paging
> + * @fw_paging_block: dma memory info
> + * @fw_paging_size: page size
> + */
> +struct iwm_fw_paging {
> +     struct iwm_dma_info fw_paging_block;
> +     uint32_t fw_paging_size;
> +};
> +
>  #define IWM_TX_RING_COUNT    256
>  #define IWM_TX_RING_LOMARK   192
>  #define IWM_TX_RING_HIMARK   224
> @@ -486,6 +497,14 @@ struct iwm_softc {
>  
>       int host_interrupt_operation_mode;
>       int sc_ltr_enabled;
> +
> +     /*
> +      * Paging parameters - All of the parameters should be set by the
> +      * opmode when paging is enabled
> +      */
> +     struct iwm_fw_paging fw_paging_db[IWM_NUM_OF_FW_PAGING_BLOCKS];
> +     uint16_t num_of_paging_blk;
> +     uint16_t num_of_pages_in_last_blk;
>  
>  #if NBPFILTER > 0
>       caddr_t                 sc_drvbpf;
> 
> 

Reply via email to