#ifdef __KERNEL__ #include #else #include #include #include #include #include #endif #include #ifndef CUDBG_LITE #include #include #include #include #include #include #include #endif #include #include enum { SF_ATTEMPTS = 10, /* max retries for SF operations */ /* flash command opcodes */ SF_PROG_PAGE = 2, /* program page */ SF_WR_DISABLE = 4, /* disable writes */ SF_RD_STATUS = 5, /* read status register */ SF_WR_ENABLE = 6, /* enable writes */ SF_RD_DATA_FAST = 0xb, /* read flash */ SF_RD_ID = 0x9f, /* read ID */ SF_ERASE_SECTOR = 0xd8, /* erase sector */ }; #if 0 /*** Flash Layout ***/ OPROM_START_SEC 0 /* First flash sector for Option-ROM */ NUM_OPROM_SEC 7 /* Number of flash sectors for Option-ROM */ FW_START_SEC 8 /* First flash sector for Firmware */ NUM_FW_SEC 16 /* Number of flash sectors for Firmware */ BOOT_CONFIG_START_SEC 7 /* First flash sector for config utility i params */ NUM_BOOT_CONFIG_SEC 1 /* Number of flash sectors for config utility params */ HW_CONFIG_START_SEC 31 /* First flash sector for hardware config file */ NUM_HW_CONFIG_SEC 1 /* Number of flash sectors for hardware config file */ UTIL_START_SEC 7 /* First flash sector for Config Utility */ FCOE_BOOT_INFO_SEC 30 /* First flash sector for FCOE BOOT INFO to OS */ NUM_FCOE_BOOT_INFO_SEC 1 /* Number of flash sectors for FCOE BOOT INFO to OS */ ISCSI_BOOT_INFO_SEC 29 /* First flash sector for ISCSI BOOT INFO to OS */ NUM_ISCSI_BOOT_INFO_SEC 1 /* Number of flash sectors for ISCSI BOOT INFO to OS */ VPDINIT_BOOT_INFO_SEC 26 /* First flash sector for VPD INIT */ NUM_VPDINIT_BOOT_SEC 1 /* Number of flash sectors for VPD INIT */ #endif /* cudbg is writing to second half of the flash 2 MB to 4 MB */ extern struct cudbg_flash_sec_info sec_info; int write_flash(struct adapter *adap, u32 start_sec, void *data, u32 size); int read_flash(struct adapter *adap, u32 start_sec , void *data, u32 size, u32 start_address); void update_skip_size(u32 size) { sec_info.skip_size += size; } u32 get_skip_size(void) { return sec_info.skip_size; } void set_sector_availability(int sector_nu, int avail) { sector_nu -= CUDBG_START_SEC; if (avail) set_dbg_bitmap(sec_info.sec_bitmap, sector_nu); else reset_dbg_bitmap(sec_info.sec_bitmap, sector_nu); } /* This function will return empty sector available for filling */ int find_empty_sec(void) { int i, index, bit; for (i = CUDBG_START_SEC; i < CUDBG_SF_MAX_SECTOR; i++) { index = (i - CUDBG_START_SEC) / 8; bit = (i - CUDBG_START_SEC) % 8; if (!(sec_info.sec_bitmap[index] & (1 << bit))) return i; } return CUDBG_STATUS_FLASH_FULL; } /* This function will get header initially. If header is already there * then it will update that header */ void update_headers(void *handle, struct cudbg_buffer *dbg_buff, u64 timestamp, u32 cur_entity_hdr_offset, u32 start_offset, u32 ext_size) { void *sec_hdr; struct cudbg_hdr *cudbg_hdr; struct cudbg_flash_hdr *flash_hdr; struct cudbg_entity_hdr *entity_hdr; u32 hdr_offset; u32 data_hdr_size; u32 total_hdr_size; u32 sec_hdr_start_addr; data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) + sizeof(struct cudbg_hdr); total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr); sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size; sec_hdr = sec_info.sec_data + sec_hdr_start_addr; flash_hdr = (struct cudbg_flash_hdr *)(sec_hdr); cudbg_hdr = (struct cudbg_hdr *)dbg_buff->data; /* initially initialize flash hdr and copy all data headers and * in next calling (else part) copy only current entity header */ if ((start_offset - sec_info.skip_size) == data_hdr_size) { flash_hdr->signature = CUDBG_FL_SIGNATURE; flash_hdr->major_ver = CUDBG_FL_MAJOR_VERSION; flash_hdr->minor_ver = CUDBG_FL_MINOR_VERSION; flash_hdr->build_ver = CUDBG_FL_BUILD_VERSION; flash_hdr->hdr_len = sizeof(struct cudbg_flash_hdr); hdr_offset = sizeof(struct cudbg_flash_hdr); memcpy((void *)((char *)sec_hdr + hdr_offset), (void *)((char *)dbg_buff->data), data_hdr_size); } else memcpy((void *)((char *)sec_hdr + sizeof(struct cudbg_flash_hdr) + cur_entity_hdr_offset), (void *)((char *)dbg_buff->data + cur_entity_hdr_offset), sizeof(struct cudbg_entity_hdr)); hdr_offset = data_hdr_size + sizeof(struct cudbg_flash_hdr); flash_hdr->data_len = cudbg_hdr->data_len - sec_info.skip_size; flash_hdr->timestamp = timestamp; entity_hdr = (struct cudbg_entity_hdr *)((char *)sec_hdr + sizeof(struct cudbg_flash_hdr) + cur_entity_hdr_offset); /* big entity like mc need to be skipped */ entity_hdr->start_offset -= sec_info.skip_size; cudbg_hdr = (struct cudbg_hdr *)((char *)sec_hdr + sizeof(struct cudbg_flash_hdr)); cudbg_hdr->data_len = flash_hdr->data_len; flash_hdr->data_len += ext_size; } /* Write CUDBG data into serial flash */ int cudbg_write_flash(void *handle, u64 timestamp, void *data, u32 start_offset, u32 cur_entity_hdr_offset, u32 cur_entity_size, u32 ext_size) { struct cudbg_init *cudbg_init = NULL; struct adapter *adap = NULL; struct cudbg_flash_hdr *flash_hdr = NULL; struct cudbg_buffer *dbg_buff = (struct cudbg_buffer *)data; u32 data_hdr_size; u32 total_hdr_size; u32 tmp_size; u32 sec_data_offset; u32 sec_hdr_start_addr; u32 sec_data_size; u32 space_left; int rc = 0; int sec; cudbg_init = &(((struct cudbg_private *)handle)->dbg_init); adap = cudbg_init->adap; data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) + sizeof(struct cudbg_hdr); total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr); sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size; sec_data_size = sec_hdr_start_addr; cudbg_init->print("\tWriting %u bytes to flash\n", cur_entity_size); /* this function will get header if sec_info.sec_data does not * have any header and * will update the header if it has header */ update_headers(handle, dbg_buff, timestamp, cur_entity_hdr_offset, start_offset, ext_size); if (ext_size) { cur_entity_size += sizeof(struct cudbg_entity_hdr); start_offset = dbg_buff->offset - cur_entity_size; } flash_hdr = (struct cudbg_flash_hdr *)(sec_info.sec_data + sec_hdr_start_addr); if (flash_hdr->data_len > CUDBG_FLASH_SIZE) { rc = CUDBG_STATUS_FLASH_FULL; goto out; } space_left = CUDBG_FLASH_SIZE - flash_hdr->data_len; if (cur_entity_size > space_left) { rc = CUDBG_STATUS_FLASH_FULL; goto out; } while (cur_entity_size > 0) { sec = find_empty_sec(); if (sec_info.par_sec) { sec_data_offset = sec_info.par_sec_offset; set_sector_availability(sec_info.par_sec, 0); sec_info.par_sec = 0; sec_info.par_sec_offset = 0; } else { sec_info.cur_seq_no++; flash_hdr->sec_seq_no = sec_info.cur_seq_no; sec_data_offset = 0; } if (cur_entity_size + sec_data_offset > sec_data_size) { tmp_size = sec_data_size - sec_data_offset; } else { tmp_size = cur_entity_size; sec_info.par_sec = sec; sec_info.par_sec_offset = cur_entity_size + sec_data_offset; } memcpy((void *)((char *)sec_info.sec_data + sec_data_offset), (void *)((char *)dbg_buff->data + start_offset), tmp_size); rc = write_flash(adap, sec, sec_info.sec_data, CUDBG_SF_SECTOR_SIZE); if (rc) goto out; cur_entity_size -= tmp_size; set_sector_availability(sec, 1); start_offset += tmp_size; } out: return rc; } int write_flash(struct adapter *adap, u32 start_sec, void *data, u32 size) { unsigned int addr; unsigned int i, n; unsigned int sf_sec_size; int rc = 0; u8 *ptr = (u8 *)data; sf_sec_size = adap->params.sf_size/adap->params.sf_nsec; addr = start_sec * CUDBG_SF_SECTOR_SIZE; i = DIV_ROUND_UP(size,/* # of sectors spanned */ sf_sec_size); rc = t4_flash_erase_sectors(adap, start_sec, start_sec + i - 1); /* * If size == 0 then we're simply erasing the FLASH sectors associated * with the on-adapter OptionROM Configuration File. */ if (rc || size == 0) goto out; /* this will write to the flash up to SF_PAGE_SIZE at a time */ for (i = 0; i < size; i += SF_PAGE_SIZE) { if ((size - i) < SF_PAGE_SIZE) n = size - i; else n = SF_PAGE_SIZE; rc = t4_write_flash(adap, addr, n, ptr, 0); if (rc) goto out; addr += n; ptr += n; } return 0; out: return rc; } int cudbg_read_flash_details(void *handle, struct cudbg_flash_hdr *data) { int rc; rc = cudbg_read_flash(handle, (void *)data, sizeof(struct cudbg_flash_hdr), 0); return rc; } int cudbg_read_flash_data(void *handle, void *buf, u32 buf_size) { int rc; u32 total_hdr_size, data_header_size; void *payload = NULL; u32 payload_size = 0; data_header_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) + sizeof(struct cudbg_hdr); total_hdr_size = data_header_size + sizeof(struct cudbg_flash_hdr); /* Copy flash header to buffer */ rc = cudbg_read_flash(handle, buf, total_hdr_size, 0); if (rc != 0) goto out; payload = (char *)buf + total_hdr_size; payload_size = buf_size - total_hdr_size; /* Reading flash data to buf */ rc = cudbg_read_flash(handle, payload, payload_size, 1); if (rc != 0) goto out; out: return rc; } int cudbg_read_flash(void *handle, void *data, u32 size, int data_flag) { struct cudbg_init *cudbg_init = NULL; struct cudbg_flash_hdr flash_hdr; struct adapter *adap = NULL; u32 total_hdr_size; u32 data_hdr_size; u32 sec_hdr_start_addr; u32 tmp_size; u32 data_offset = 0; u32 i, j; int rc; cudbg_init = &(((struct cudbg_private *)handle)->dbg_init); adap = cudbg_init->adap; rc = t4_get_flash_params(adap); if (rc) { cudbg_init->print("\nGet flash params failed." "Try Again...readflash\n\n"); return rc; } data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) + sizeof(struct cudbg_hdr); total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr); sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size; if (!data_flag) { /* fill header */ if (!sec_info.max_timestamp) { /* finding max time stamp because it may * have older filled sector also */ memset(&flash_hdr, 0, sizeof(struct cudbg_flash_hdr)); rc = read_flash(adap, CUDBG_START_SEC, &flash_hdr, sizeof(struct cudbg_flash_hdr), sec_hdr_start_addr); if (flash_hdr.signature == CUDBG_FL_SIGNATURE) { sec_info.max_timestamp = flash_hdr.timestamp; } else { rc = read_flash(adap, CUDBG_START_SEC + 1, &flash_hdr, sizeof(struct cudbg_flash_hdr), sec_hdr_start_addr); if (flash_hdr.signature == CUDBG_FL_SIGNATURE) sec_info.max_timestamp = flash_hdr.timestamp; else { cudbg_init->print("\n\tNo cudbg dump "\ "found in flash\n\n"); return CUDBG_STATUS_NO_SIGNATURE; } } /* finding max sequence number because max sequenced * sector has updated header */ for (i = CUDBG_START_SEC; i < CUDBG_SF_MAX_SECTOR; i++) { memset(&flash_hdr, 0, sizeof(struct cudbg_flash_hdr)); rc = read_flash(adap, i, &flash_hdr, sizeof(struct cudbg_flash_hdr), sec_hdr_start_addr); if (flash_hdr.signature == CUDBG_FL_SIGNATURE && sec_info.max_timestamp == flash_hdr.timestamp && sec_info.max_seq_no <= flash_hdr.sec_seq_no) { if (sec_info.max_seq_no == flash_hdr.sec_seq_no) { if (sec_info.hdr_data_len < flash_hdr.data_len) sec_info.max_seq_sec = i; } else { sec_info.max_seq_sec = i; sec_info.hdr_data_len = flash_hdr.data_len; } sec_info.max_seq_no = flash_hdr.sec_seq_no; } } } rc = read_flash(adap, sec_info.max_seq_sec, (struct cudbg_flash_hdr *)data, size, sec_hdr_start_addr); if (rc) cudbg_init->print("Read flash header failed, rc %d\n", rc); return rc; } /* finding sector sequence sorted */ for (i = 1; i <= sec_info.max_seq_no; i++) { for (j = CUDBG_START_SEC; j < CUDBG_SF_MAX_SECTOR; j++) { memset(&flash_hdr, 0, sizeof(struct cudbg_flash_hdr)); rc = read_flash(adap, j, &flash_hdr, sizeof(struct cudbg_flash_hdr), sec_hdr_start_addr); if (flash_hdr.signature == CUDBG_FL_SIGNATURE && sec_info.max_timestamp == flash_hdr.timestamp && flash_hdr.sec_seq_no == i) { if (size + total_hdr_size > CUDBG_SF_SECTOR_SIZE) tmp_size = CUDBG_SF_SECTOR_SIZE - total_hdr_size; else tmp_size = size; if ((i != sec_info.max_seq_no) || (i == sec_info.max_seq_no && j == sec_info.max_seq_sec)){ /* filling data buffer with sector data * except sector header */ rc = read_flash(adap, j, (void *)((char *)data + data_offset), tmp_size, 0); data_offset += (tmp_size); size -= (tmp_size); break; } } } } return rc; } int read_flash(struct adapter *adap, u32 start_sec , void *data, u32 size, u32 start_address) { unsigned int addr, i, n; int rc; u32 *ptr = (u32 *)data; addr = start_sec * CUDBG_SF_SECTOR_SIZE + start_address; size = size / 4; for (i = 0; i < size; i += SF_PAGE_SIZE) { if ((size - i) < SF_PAGE_SIZE) n = size - i; else n = SF_PAGE_SIZE; rc = t4_read_flash(adap, addr, n, ptr, 0); if (rc) goto out; addr = addr + (n*4); ptr += n; } return 0; out: return rc; } #ifdef CUDBG_UTILS static int cudbg_fw_restart(struct adapter *adap, unsigned int mbox, int reset) { if (reset) { /* * Since we're directing the RESET instead of the firmware * doing it automatically, we need to clear the PCIE_FW.HALT * bit. */ t4_set_reg_field(adap, A_PCIE_FW, F_PCIE_FW_HALT, 0); /* * If we've been given a valid mailbox, first try to get the * firmware to do the RESET. If that works, great and we can * return success. Otherwise, if we haven't been given a * valid mailbox or the RESET command failed, fall back to * hitting the chip with a hammer. */ if (mbox <= M_PCIE_FW_MASTER) { t4_set_reg_field(adap, A_CIM_BOOT_CFG, F_UPCRST, 0); msleep(100); if (t4_fw_reset(adap, mbox, F_PIORST | F_PIORSTMODE) == 0) return 0; } t4_write_reg(adap, A_PL_RST, F_PIORST | F_PIORSTMODE); msleep(2000); } else { int ms; t4_set_reg_field(adap, A_CIM_BOOT_CFG, F_UPCRST, 0); for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) { if (!(t4_read_reg(adap, A_PCIE_FW) & F_PCIE_FW_HALT)) return FW_SUCCESS; msleep(100); ms += 100; } return -ETIMEDOUT; } return 0; } static int cudbg_fw_halt(struct adapter *adap, unsigned int mbox, int force) { int ret = 0; /* * If a legitimate mailbox is provided, issue a RESET command * with a HALT indication. */ if (mbox <= M_PCIE_FW_MASTER) { struct fw_reset_cmd c; memset(&c, 0, sizeof(c)); c.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_RESET_CMD) | F_FW_CMD_REQUEST | F_FW_CMD_WRITE); c.retval_len16 = cpu_to_be32(FW_LEN16(c)); c.val = cpu_to_be32(F_PIORST | F_PIORSTMODE); c.halt_pkd = cpu_to_be32(F_FW_RESET_CMD_HALT); ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); } /* * Normally we won't complete the operation if the firmware RESET * command fails but if our caller insists we'll go ahead and put the * uP into RESET. This can be useful if the firmware is hung or even * missing ... We'll have to take the risk of putting the uP into * RESET without the cooperation of firmware in that case. * * We also force the firmware's HALT flag to be on in case we bypassed * the firmware RESET command above or we're dealing with old firmware * which doesn't have the HALT capability. This will serve as a flag * for the incoming firmware to know that it's coming out of a HALT * rather than a RESET ... if it's new enough to understand that ... */ if (ret == 0 || force) { t4_set_reg_field(adap, A_CIM_BOOT_CFG, F_UPCRST, F_UPCRST); t4_set_reg_field(adap, A_PCIE_FW, F_PCIE_FW_HALT, F_PCIE_FW_HALT); } /* * And we always return the result of the firmware RESET command * even when we force the uP into RESET ... */ return ret; } static int cudbg_fw_upgrade(struct cudbg_init *cudbg_init, unsigned int mbox, const u8 *fw_data, unsigned int size) { struct adapter *adap = cudbg_init->adap; const struct fw_hdr *hdr = (const struct fw_hdr *)fw_data; int reset, ret; if (!((is_t4(adap->params.chip) && hdr->chip == FW_HDR_CHIP_T4) || (is_t5(adap->params.chip) && hdr->chip == FW_HDR_CHIP_T5) || (is_t6(adap->params.chip) && hdr->chip == FW_HDR_CHIP_T6))) return CUDBG_STATUS_NO_SIGNATURE; /* Halt firmware and force uP into RESET. * Continue flashing firmware regardless of whether halt * succeeds or not. */ ret = cudbg_fw_halt(adap, mbox, 1); if (ret) cudbg_init->print("Halting Firmware failed, ret = %d. Continuing.\n", ret); ret = t4_load_fw(adap, fw_data, size, 0); if (ret < 0) { if (ret == -EINVAL) ret = CUDBG_STATUS_NO_SIGNATURE; else if (ret == -EFBIG) ret = CUDBG_STATUS_OUTBUFF_OVERFLOW; else ret = CUDBG_STATUS_FLASH_WRITE_FAIL; return ret; } /* * Older versions of the firmware don't understand the new * PCIE_FW.HALT flag and so won't know to perform a RESET when they * restart. So for newly loaded older firmware we'll have to do the * RESET for it so it starts up on a clean slate. We can tell if * the newly loaded firmware will handle this right by checking * its header flags to see if it advertises the capability. */ reset = ((be32_to_cpu(hdr->flags) & FW_HDR_FLAGS_RESET_HALT) == 0); ret = cudbg_fw_restart(adap, mbox, reset); if (ret) ret = CUDBG_SYSTEM_ERROR; return ret; } int cudbg_loadfw(void *handle, uint8_t *buf, size_t len) { struct cudbg_init *cudbg_init = &(((struct cudbg_private *)handle)->dbg_init); struct adapter *adap = cudbg_init->adap; unsigned int mbox = M_PCIE_FW_MASTER + 1; u8 *fw_data; int rc; rc = t4_get_flash_params(adap); if (rc < 0) { cudbg_debug(cudbg_init, "\nGet flash params failed.\n\n"); rc = CUDBG_STATUS_FLASH_READ_FAIL; return rc; } if ((adap->flags & FULL_INIT_DONE)) { cudbg_debug(cudbg_init, "Full init done for adap"); mbox = adap->mbox; } fw_data = buf; rc = cudbg_fw_upgrade(cudbg_init, mbox, fw_data, len); if (rc) cudbg_debug(cudbg_init, "FW flash failed, rc %d\n", rc); return rc; } void cudbg_crashfw(void *handle) { struct cudbg_init *cudbg_init = &(((struct cudbg_private *)handle)->dbg_init); struct adapter *adap = cudbg_init->adap; struct fw_ldst_cmd c; memset(&c, 0, sizeof(c)); c.op_to_addrspace = cpu_to_be32(V_FW_CMD_OP(FW_LDST_CMD) | F_FW_CMD_REQUEST | F_FW_CMD_READ | V_FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_FIRMWARE)); c.cycles_to_len16 = cpu_to_be32(FW_LEN16(c)); c.u.addrval.addr = cpu_to_be32(0xfffffff0); c.u.addrval.val = cpu_to_be32(0xffffffff); t4_wr_mbox(adap, adap->mbox, &c, sizeof(c), NULL); } void cudbg_restartfw(void *handle) { struct cudbg_init *cudbg_init = &(((struct cudbg_private *)handle)->dbg_init); struct adapter *adap = cudbg_init->adap; unsigned int mbox = M_PCIE_FW_MASTER + 1; cudbg_fw_restart(adap, mbox, 1); } static int validate_fw(struct cudbg_init *cudbg_init, struct fw_hdr *hdr, u32 buf_len) { struct adapter *adap = cudbg_init->adap; u32 size; size = be16_to_cpu(hdr->len512) * 512; if (!size) { cudbg_debug(cudbg_init, "FW image has no data\n"); return -EINVAL; } if (size & 511) { cudbg_debug(cudbg_init, "FW image size not multiple of 512 bytes\n"); return -EINVAL; } if (size > buf_len) { cudbg_debug(cudbg_init, "FW image too large, max is %u bytes\n", buf_len); return -EFBIG; } if (!((is_t4(adap->params.chip) && hdr->chip == FW_HDR_CHIP_T4) || (is_t5(adap->params.chip) && hdr->chip == FW_HDR_CHIP_T5) || (is_t6(adap->params.chip) && hdr->chip == FW_HDR_CHIP_T6))) { cudbg_debug(cudbg_init, "FW doesn't match chip\n"); return -EINVAL; } return 0; } int cudbg_fw_read(struct cudbg_init *cudbg_init, u32 *buf, u32 *len) { struct adapter *adap = cudbg_init->adap; u32 addr, size, chunk_size; struct fw_hdr *hdr; u32 csum, i; u32 *bufp; int ret; bufp = buf; /* Read the first chunk to get the size of the firmware * on card */ addr = FLASH_FW_START; chunk_size = SF_PAGE_SIZE / sizeof(u32); ret = t4_read_flash(adap, addr, chunk_size, bufp, 1); if (ret) goto out; hdr = (struct fw_hdr *)bufp; ret = validate_fw(cudbg_init, hdr, *len); if (ret) { ret = CUDBG_STATUS_CORRUPTED; goto out; } size = be16_to_cpu(hdr->len512) * 512; /* Remove the first chunk already read above */ addr += SF_PAGE_SIZE; size -= SF_PAGE_SIZE; bufp += chunk_size; /* Start reading firmware image SF_PAGE_SIZE at a time */ while (size) { ret = t4_read_flash(adap, addr, chunk_size, bufp, 1); if (ret) goto out; addr += SF_PAGE_SIZE; size -= SF_PAGE_SIZE; bufp += chunk_size; } /* Record the actual size of firmware read */ *len = be16_to_cpu(hdr->len512) * 512; /* Validate firmware image */ for (csum = 0, i = 0; i < *len / sizeof(csum); i++) csum += be32_to_cpu(buf[i]); if (csum != 0xffffffff) { cudbg_debug(cudbg_init, "corrupted firmware image, checksum %#x\n", csum); ret = CUDBG_STATUS_CORRUPTED; goto out; } out: return ret; } /* cudbg_readfw() - Read firmware image on card. * handle: cudbg handle * buf: the destination buffer to write firmware image. * len: the total size of the firmware on card. * * Reads firmware image on card into @buf provided. * Record the total size of the firmware into @len. */ int cudbg_readfw(void *handle, u8 *buf, u32 *len) { struct cudbg_init *cudbg_init = &(((struct cudbg_private *)handle)->dbg_init); struct adapter *adap = cudbg_init->adap; int rc; /* Get flash parameters and record them */ rc = t4_get_flash_params(adap); if (rc < 0) { cudbg_debug(cudbg_init, "\nGet flash params failed. rc: %d\n\n", rc); rc = CUDBG_STATUS_FLASH_READ_FAIL; goto out; } /* Read firmware image on card */ rc = cudbg_fw_read(cudbg_init, (u32 *)buf, len); if (rc) { cudbg_debug(cudbg_init, "\nReading FW from flash failed. rc: %d\n\n", rc); if (rc != CUDBG_STATUS_CORRUPTED) rc = CUDBG_STATUS_FLASH_READ_FAIL; goto out; } out: return rc; } /** * The following routines will implement eeprom read/write */ #define VPD_DIAGSRESULT_START 0x1ae0 #define VPD_DIAGSRESULT_END 0x1bb9 #define EEPROM_STAT_ADDR 0x7bfc static int eeprom_ptov(unsigned int phys_addr, unsigned int fn, unsigned int sz) { fn *= sz; if (phys_addr < 1024) return phys_addr + (31 << 10); if (phys_addr < 1024 + fn) return fn + phys_addr - 1024; if (phys_addr < EEPROMSIZE) return phys_addr - 1024 - fn; if (phys_addr < EEPROMVSIZE) return phys_addr - 1024; return -EINVAL; } static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v) { int vaddr = eeprom_ptov(phys_addr, adap->pf, EEPROMPFSIZE); if (vaddr >= 0) vaddr = t4_seeprom_read(adap, vaddr, v); return vaddr < 0 ? vaddr : 0; } static int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v) { int vaddr = 0; if ((phys_addr >= VPD_DIAGSRESULT_START) && (phys_addr <= VPD_DIAGSRESULT_END)) return 0; vaddr = eeprom_ptov(phys_addr, adap->pf, EEPROMPFSIZE); if (vaddr == EEPROM_STAT_ADDR) return 0; if (vaddr >= 0) vaddr = t4_seeprom_write(adap, vaddr, v); return vaddr < 0 ? vaddr : 0; } int cudbg_get_eeprom(void *handle, u8 *data, int addr, int len) { struct cudbg_init *cudbg_init = &(((struct cudbg_private *)handle)->dbg_init); struct adapter *adapter = cudbg_init->adap; u8 *buf = t4_os_alloc(EEPROMSIZE); int i, err = 0; if (!buf) return -ENOMEM; for (i = addr & ~3; !err && i < addr + len; i += 4) err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]); if (!err) memcpy(data, buf + addr, len); t4_os_free(buf); return err; } int cudbg_set_eeprom(void *handle, u8 *data, int offset, int len) { struct cudbg_init *cudbg_init = &(((struct cudbg_private *)handle)->dbg_init); struct adapter *adapter = cudbg_init->adap; u32 aligned_offset, aligned_len, *p; int err = 0; u8 *buf; aligned_offset = offset & ~3; aligned_len = (len + (offset & 3) + 3) & ~3; if (aligned_offset != offset || aligned_len != len) { /* RMW possibly needed for first or last words. */ buf = t4_os_alloc(aligned_len); if (!buf) { err = -ENOMEM; goto out; } memset(buf, 0, aligned_len); err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf); if (!err && aligned_len > 4) err = eeprom_rd_phys(adapter, aligned_offset + aligned_len - 4, (u32 *)&buf[aligned_len - 4]); if (err) goto out; memcpy(buf + (offset & 3), data, len); } else buf = data; cudbg_init->print("Writing to adapter with start_addr=%d, len=%d\n", offset, len); err = t4_seeprom_wp(adapter, false); if (err) goto out; for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) { err = eeprom_wr_phys(adapter, aligned_offset, *p); aligned_offset += 4; } if (!err) err = t4_seeprom_wp(adapter, true); out: if (buf != NULL && buf != data) t4_os_free(buf); return err; } #endif