cxgb4: add support to flash boot image
authorVishal Kulkarni <vishal@chelsio.com>
Thu, 18 Jun 2020 06:05:54 +0000 (11:35 +0530)
committerDavid S. Miller <davem@davemloft.net>
Fri, 19 Jun 2020 03:49:55 +0000 (20:49 -0700)
Update set_flash to flash boot image to flash region

Signed-off-by: Vishal Kulkarni <vishal@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_regs.h

index b49d16a..7b5f186 100644 (file)
@@ -142,6 +142,52 @@ enum cc_fec {
 enum {
        CXGB4_ETHTOOL_FLASH_FW = 1,
        CXGB4_ETHTOOL_FLASH_PHY = 2,
+       CXGB4_ETHTOOL_FLASH_BOOT = 3,
+};
+
+struct cxgb4_pcir_data {
+       __le32 signature;       /* Signature. The string "PCIR" */
+       __le16 vendor_id;       /* Vendor Identification */
+       __le16 device_id;       /* Device Identification */
+       __u8 vital_product[2];  /* Pointer to Vital Product Data */
+       __u8 length[2];         /* PCIR Data Structure Length */
+       __u8 revision;          /* PCIR Data Structure Revision */
+       __u8 class_code[3];     /* Class Code */
+       __u8 image_length[2];   /* Image Length. Multiple of 512B */
+       __u8 code_revision[2];  /* Revision Level of Code/Data */
+       __u8 code_type;
+       __u8 indicator;
+       __u8 reserved[2];
+};
+
+/* BIOS boot headers */
+struct cxgb4_pci_exp_rom_header {
+       __le16 signature;       /* ROM Signature. Should be 0xaa55 */
+       __u8 reserved[22];      /* Reserved per processor Architecture data */
+       __le16 pcir_offset;     /* Offset to PCI Data Structure */
+};
+
+/* Legacy PCI Expansion ROM Header */
+struct legacy_pci_rom_hdr {
+       __u8 signature[2];      /* ROM Signature. Should be 0xaa55 */
+       __u8 size512;           /* Current Image Size in units of 512 bytes */
+       __u8 initentry_point[4];
+       __u8 cksum;             /* Checksum computed on the entire Image */
+       __u8 reserved[16];      /* Reserved */
+       __le16 pcir_offset;     /* Offset to PCI Data Struture */
+};
+
+#define CXGB4_HDR_CODE1 0x00
+#define CXGB4_HDR_CODE2 0x03
+#define CXGB4_HDR_INDI 0x80
+
+/* BOOT constants */
+enum {
+       BOOT_SIZE_INC = 512,
+       BOOT_SIGNATURE = 0xaa55,
+       BOOT_MIN_SIZE = sizeof(struct cxgb4_pci_exp_rom_header),
+       BOOT_MAX_SIZE = 1024 * BOOT_SIZE_INC,
+       PCIR_SIGNATURE = 0x52494350
 };
 
 struct port_stats {
@@ -1998,6 +2044,8 @@ void t4_register_netevent_notifier(void);
 int t4_i2c_rd(struct adapter *adap, unsigned int mbox, int port,
              unsigned int devid, unsigned int offset,
              unsigned int len, u8 *buf);
+int t4_load_boot(struct adapter *adap, u8 *boot_data,
+                unsigned int boot_addr, unsigned int size);
 void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq, struct sge_fl *fl);
 void free_tx_desc(struct adapter *adap, struct sge_txq *q,
                  unsigned int n, bool unmap);
index 7118ba0..2964525 100644 (file)
@@ -27,6 +27,7 @@ static const char * const flash_region_strings[] = {
        "All",
        "Firmware",
        "PHY Firmware",
+       "Boot",
 };
 
 static const char stats_strings[][ETH_GSTRING_LEN] = {
@@ -1241,6 +1242,28 @@ out:
        return err;
 }
 
+static int cxgb4_ethtool_flash_boot(struct net_device *netdev,
+                                   const u8 *bdata, u32 size)
+{
+       struct adapter *adap = netdev2adap(netdev);
+       unsigned int offset;
+       u8 *data;
+       int ret;
+
+       data = kmemdup(bdata, size, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       offset = OFFSET_G(t4_read_reg(adap, PF_REG(0, PCIE_PF_EXPROM_OFST_A)));
+
+       ret = t4_load_boot(adap, data, offset, size);
+       if (ret)
+               dev_err(adap->pdev_dev, "Failed to load boot image\n");
+
+       kfree(data);
+       return ret;
+}
+
 #define CXGB4_PHY_SIG 0x130000ea
 
 static int cxgb4_validate_phy_image(const u8 *data, u32 *size)
@@ -1310,6 +1333,9 @@ static int cxgb4_ethtool_flash_region(struct net_device *netdev,
        case CXGB4_ETHTOOL_FLASH_PHY:
                ret = cxgb4_ethtool_flash_phy(netdev, data, size);
                break;
+       case CXGB4_ETHTOOL_FLASH_BOOT:
+               ret = cxgb4_ethtool_flash_boot(netdev, data, size);
+               break;
        default:
                ret = -EOPNOTSUPP;
                break;
@@ -1339,10 +1365,40 @@ static int cxgb4_validate_fw_image(const u8 *data, u32 *size)
        return 0;
 }
 
+static int cxgb4_validate_boot_image(const u8 *data, u32 *size)
+{
+       struct cxgb4_pci_exp_rom_header *exp_header;
+       struct cxgb4_pcir_data *pcir_header;
+       struct legacy_pci_rom_hdr *header;
+       const u8 *cur_header = data;
+       u16 pcir_offset;
+
+       exp_header = (struct cxgb4_pci_exp_rom_header *)data;
+
+       if (le16_to_cpu(exp_header->signature) != BOOT_SIGNATURE)
+               return -EINVAL;
+
+       if (size) {
+               do {
+                       header = (struct legacy_pci_rom_hdr *)cur_header;
+                       pcir_offset = le16_to_cpu(header->pcir_offset);
+                       pcir_header = (struct cxgb4_pcir_data *)(cur_header +
+                                     pcir_offset);
+
+                       *size += header->size512 * 512;
+                       cur_header += header->size512 * 512;
+               } while (!(pcir_header->indicator & CXGB4_HDR_INDI));
+       }
+
+       return 0;
+}
+
 static int cxgb4_ethtool_get_flash_region(const u8 *data, u32 *size)
 {
        if (!cxgb4_validate_fw_image(data, size))
                return CXGB4_ETHTOOL_FLASH_FW;
+       if (!cxgb4_validate_boot_image(data, size))
+               return CXGB4_ETHTOOL_FLASH_BOOT;
        if (!cxgb4_validate_phy_image(data, size))
                return CXGB4_ETHTOOL_FLASH_PHY;
 
index 1c8068c..ccb550c 100644 (file)
@@ -10481,3 +10481,190 @@ int t4_set_vlan_acl(struct adapter *adap, unsigned int mbox, unsigned int vf,
 
        return t4_wr_mbox(adap, adap->mbox, &vlan_cmd, sizeof(vlan_cmd), NULL);
 }
+
+/**
+ *     modify_device_id - Modifies the device ID of the Boot BIOS image
+ *     @device_id: the device ID to write.
+ *     @boot_data: the boot image to modify.
+ *
+ *     Write the supplied device ID to the boot BIOS image.
+ */
+static void modify_device_id(int device_id, u8 *boot_data)
+{
+       struct cxgb4_pcir_data *pcir_header;
+       struct legacy_pci_rom_hdr *header;
+       u8 *cur_header = boot_data;
+       u16 pcir_offset;
+
+        /* Loop through all chained images and change the device ID's */
+       do {
+               header = (struct legacy_pci_rom_hdr *)cur_header;
+               pcir_offset = le16_to_cpu(header->pcir_offset);
+               pcir_header = (struct cxgb4_pcir_data *)(cur_header +
+                             pcir_offset);
+
+               /**
+                * Only modify the Device ID if code type is Legacy or HP.
+                * 0x00: Okay to modify
+                * 0x01: FCODE. Do not modify
+                * 0x03: Okay to modify
+                * 0x04-0xFF: Do not modify
+                */
+               if (pcir_header->code_type == CXGB4_HDR_CODE1) {
+                       u8 csum = 0;
+                       int i;
+
+                       /**
+                        * Modify Device ID to match current adatper
+                        */
+                       pcir_header->device_id = cpu_to_le16(device_id);
+
+                       /**
+                        * Set checksum temporarily to 0.
+                        * We will recalculate it later.
+                        */
+                       header->cksum = 0x0;
+
+                       /**
+                        * Calculate and update checksum
+                        */
+                       for (i = 0; i < (header->size512 * 512); i++)
+                               csum += cur_header[i];
+
+                       /**
+                        * Invert summed value to create the checksum
+                        * Writing new checksum value directly to the boot data
+                        */
+                       cur_header[7] = -csum;
+
+               } else if (pcir_header->code_type == CXGB4_HDR_CODE2) {
+                       /**
+                        * Modify Device ID to match current adatper
+                        */
+                       pcir_header->device_id = cpu_to_le16(device_id);
+               }
+
+               /**
+                * Move header pointer up to the next image in the ROM.
+                */
+               cur_header += header->size512 * 512;
+       } while (!(pcir_header->indicator & CXGB4_HDR_INDI));
+}
+
+/**
+ *     t4_load_boot - download boot flash
+ *     @adap: the adapter
+ *     @boot_data: the boot image to write
+ *     @boot_addr: offset in flash to write boot_data
+ *     @size: image size
+ *
+ *     Write the supplied boot image to the card's serial flash.
+ *     The boot image has the following sections: a 28-byte header and the
+ *     boot image.
+ */
+int t4_load_boot(struct adapter *adap, u8 *boot_data,
+                unsigned int boot_addr, unsigned int size)
+{
+       unsigned int sf_sec_size = adap->params.sf_size / adap->params.sf_nsec;
+       unsigned int boot_sector = (boot_addr * 1024);
+       struct cxgb4_pci_exp_rom_header *header;
+       struct cxgb4_pcir_data *pcir_header;
+       int pcir_offset;
+       unsigned int i;
+       u16 device_id;
+       int ret, addr;
+
+       /**
+        * Make sure the boot image does not encroach on the firmware region
+        */
+       if ((boot_sector + size) >> 16 > FLASH_FW_START_SEC) {
+               dev_err(adap->pdev_dev, "boot image encroaching on firmware region\n");
+               return -EFBIG;
+       }
+
+       /* Get boot header */
+       header = (struct cxgb4_pci_exp_rom_header *)boot_data;
+       pcir_offset = le16_to_cpu(header->pcir_offset);
+       /* PCIR Data Structure */
+       pcir_header = (struct cxgb4_pcir_data *)&boot_data[pcir_offset];
+
+       /**
+        * Perform some primitive sanity testing to avoid accidentally
+        * writing garbage over the boot sectors.  We ought to check for
+        * more but it's not worth it for now ...
+        */
+       if (size < BOOT_MIN_SIZE || size > BOOT_MAX_SIZE) {
+               dev_err(adap->pdev_dev, "boot image too small/large\n");
+               return -EFBIG;
+       }
+
+       if (le16_to_cpu(header->signature) != BOOT_SIGNATURE) {
+               dev_err(adap->pdev_dev, "Boot image missing signature\n");
+               return -EINVAL;
+       }
+
+       /* Check PCI header signature */
+       if (le32_to_cpu(pcir_header->signature) != PCIR_SIGNATURE) {
+               dev_err(adap->pdev_dev, "PCI header missing signature\n");
+               return -EINVAL;
+       }
+
+       /* Check Vendor ID matches Chelsio ID*/
+       if (le16_to_cpu(pcir_header->vendor_id) != PCI_VENDOR_ID_CHELSIO) {
+               dev_err(adap->pdev_dev, "Vendor ID missing signature\n");
+               return -EINVAL;
+       }
+
+       /**
+        * The boot sector is comprised of the Expansion-ROM boot, iSCSI boot,
+        * and Boot configuration data sections. These 3 boot sections span
+        * sectors 0 to 7 in flash and live right before the FW image location.
+        */
+       i = DIV_ROUND_UP(size ? size : FLASH_FW_START,  sf_sec_size);
+       ret = t4_flash_erase_sectors(adap, boot_sector >> 16,
+                                    (boot_sector >> 16) + i - 1);
+
+       /**
+        * If size == 0 then we're simply erasing the FLASH sectors associated
+        * with the on-adapter option ROM file
+        */
+       if (ret || size == 0)
+               goto out;
+       /* Retrieve adapter's device ID */
+       pci_read_config_word(adap->pdev, PCI_DEVICE_ID, &device_id);
+       /* Want to deal with PF 0 so I strip off PF 4 indicator */
+       device_id = device_id & 0xf0ff;
+
+        /* Check PCIE Device ID */
+       if (le16_to_cpu(pcir_header->device_id) != device_id) {
+               /**
+                * Change the device ID in the Boot BIOS image to match
+                * the Device ID of the current adapter.
+                */
+               modify_device_id(device_id, boot_data);
+       }
+
+       /**
+        * Skip over the first SF_PAGE_SIZE worth of data and write it after
+        * we finish copying the rest of the boot image. This will ensure
+        * that the BIOS boot header will only be written if the boot image
+        * was written in full.
+        */
+       addr = boot_sector;
+       for (size -= SF_PAGE_SIZE; size; size -= SF_PAGE_SIZE) {
+               addr += SF_PAGE_SIZE;
+               boot_data += SF_PAGE_SIZE;
+               ret = t4_write_flash(adap, addr, SF_PAGE_SIZE, boot_data);
+               if (ret)
+                       goto out;
+       }
+
+       ret = t4_write_flash(adap, boot_sector, SF_PAGE_SIZE,
+                            (const u8 *)header);
+
+out:
+       if (ret)
+               dev_err(adap->pdev_dev, "boot image load failed, error %d\n",
+                       ret);
+       return ret;
+}
index 4a9fcd6..4b69755 100644 (file)
 #define AIVEC_V(x) ((x) << AIVEC_S)
 
 #define PCIE_PF_CLI_A  0x44
+
+#define PCIE_PF_EXPROM_OFST_A 0x4c
+#define OFFSET_S    10
+#define OFFSET_M    0x3fffU
+#define OFFSET_G(x) (((x) >> OFFSET_S) & OFFSET_M)
+
 #define PCIE_INT_CAUSE_A       0x3004
 
 #define UNXSPLCPLERR_S    29