/** * NAND specific update routine. Handles erasing the previous * image if it exists. * * @param image_addr Address of image in DRAM. Always * has an image header. * * @return 0 on success * 1 on failure */ int do_bootloader_update_nand(uint32_t image_addr) { const bootloader_header_t *new_header; const bootloader_header_t *header; int chip = oct_nand_get_cur_chip(); int page_size = cvmx_nand_get_page_size(chip); int oob_size = octeon_nand_get_oob_size(chip); int pages_per_block = cvmx_nand_get_pages_per_block(chip); int bytes; uint64_t block_size = page_size * pages_per_block; uint64_t nand_addr = block_size; uint64_t buf_storage[2200 / 8] = { 0 }; unsigned char *buf = (unsigned char *)buf_storage; int read_size = CVMX_NAND_BOOT_ECC_BLOCK_SIZE + 8; uint64_t old_image_nand_addr = 0; int old_image_size = 0; int required_len; int required_blocks; int conseq_blank_blocks; uint64_t erase_base; uint64_t erase_end; header = (void *)buf; new_header = (void *)image_addr; if (!cvmx_nand_get_active_chips()) { puts("ERROR: No NAND Flash detected on board, can't burn " "NAND bootloader image\n"); return 1; } /* Find matching type (failsafe/normal, stage2/stage3) of image that * is currently in NAND, if present. Save location for later erasing */ while ((nand_addr = oct_nand_image_search(nand_addr, MAX_NAND_SEARCH_ADDR, new_header->image_type))) { /* Read new header */ bytes = cvmx_nand_page_read(chip, nand_addr, cvmx_ptr_to_phys(buf), read_size); if (bytes != read_size) { printf("Error reading NAND addr 0x%llx (bytes_read: %d, expected: %d)\n", nand_addr, bytes, read_size); return 1; } /* Check a few more fields from the headers */ if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_GENERIC && header->board_type != CVMX_BOARD_TYPE_GENERIC) { /* If the board type of the running image is generic, * don't do any board matching. When looking for images * in NAND to overwrite, treat generic board type images * as matching all board types. */ if (new_header->board_type != header->board_type) { puts("WARNING: A bootloader for a different " "board type was found and skipped (not erased.)\n"); /* Different board type, so skip (this is * strange to find..... */ nand_addr += ((header->hlen + header->dlen + page_size - 1) & ~(page_size - 1)); continue; } } if ((new_header->flags & BL_HEADER_FLAG_FAILSAFE) != (new_header->flags & BL_HEADER_FLAG_FAILSAFE)) { /* Not a match, so skip */ nand_addr += ((header->hlen + header->dlen + page_size - 1) & ~(page_size - 1)); continue; } /* A match, so break out */ old_image_nand_addr = nand_addr; old_image_size = header->hlen + header->dlen; printf("Found existing bootloader image of same type at NAND addr: 0x%llx\n", old_image_nand_addr); break; } /* nand_addr is either 0 (no image found), or has the address of the * image we will delete after the write of the new image. */ if (!nand_addr) puts("No existing matching bootloader found in flash\n"); /* Find a blank set of _blocks_ to put the new image in. We want * to make sure that we don't put any part of it in a block with * something else, as we want to be able to erase it later. */ required_len = new_header->hlen + new_header->dlen; required_blocks = (required_len + block_size - 1) / block_size; conseq_blank_blocks = 0; read_size = page_size + oob_size; for (nand_addr = block_size; nand_addr < MAX_NAND_SEARCH_ADDR; nand_addr += block_size) { if (oct_nand_block_is_blank(nand_addr)) { conseq_blank_blocks++; if (conseq_blank_blocks == required_blocks) { /* We have a large enough blank spot */ nand_addr -= (conseq_blank_blocks - 1) * block_size; break; } } else conseq_blank_blocks = 0; } if (nand_addr >= MAX_NAND_SEARCH_ADDR) { puts("ERROR: unable to find blank space for new bootloader\n"); return 1; } printf("New bootloader image will be written at blank address 0x%llx, length 0x%x\n", nand_addr, required_len); /* Write the new bootloader to blank location. */ if (0 > oct_nand_boot_write(nand_addr, (void *)image_addr, required_len, 0)) { puts("ERROR: error while writing new image to flash.\n"); return 1; } /* Now erase the old bootloader of the same type. * We know these are not bad NAND blocks since they have valid data * in them. */ erase_base = old_image_nand_addr & ~(block_size - 1); erase_end = ((old_image_nand_addr + old_image_size + block_size - 1) & ~(block_size - 1)); for (nand_addr = erase_base; nand_addr < erase_end; nand_addr += block_size) { if (cvmx_nand_block_erase(chip, nand_addr)) { printf("cvmx_nand_block_erase() failed, addr 0x%08llx\n", nand_addr); return 1; } } puts("Bootloader update in NAND complete.\n"); return 0; }
int do_oct_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { u64 buf_storage[CVMX_NAND_MAX_PAGE_AND_OOB_SIZE / 8]; unsigned char *buf = (unsigned char *)buf_storage; int rc; const char *cmd; if (argc < 2) goto usage; cmd = argv[1]; if (!strcmp("probe", cmd)) { rc = oct_nand_probe(); if (rc < 0) { printf("NAND flash not found\n"); } else { printf("probe found NAND flash on boot bus %d.\n", rc); } return 0; } else if (!strcmp("dump", cmd)) { /* Only dump full pages.... */ u64 page, end; u64 start_addr, len; int page_size = cvmx_nand_get_page_size(chip); int oob_size = cvmx_nand_get_oob_size(chip); if (argc < 3) goto usage; start_addr = simple_strtoull(argv[2], NULL, 16); if (argc == 4) len = simple_strtoull(argv[3], NULL, 16); else len = 0; if (!page_size) { printf("ERROR: No NAND configured (run probe)\n"); return 1; } /* Convert from addresses to pages */ page = start_addr / page_size; if (!len) end = page; else end = (start_addr + len) / page_size; while (page <= end) { /* Read the next block */ int i; int read_size = page_size + oob_size; /* Read more than required in cases when total read size * is not a multiple of 8. */ read_size = (read_size + 7) & ~0x7; int bytes = cvmx_nand_page_read(chip, page_size * page, cvmx_ptr_to_phys(buf), read_size); int pages_per_block = cvmx_nand_get_pages_per_block(chip); if (bytes != read_size) { printf("Error reading page %llu, " "bytes read: %d\n", page, bytes); return 1; } /* Dump page data */ printf("Address 0x%llx, (Block 0x%llx, page 0x%llx) " "data:\n", page * page_size, page / pages_per_block, page % pages_per_block); for (i = 0; i < page_size; i++) { if (i % 16 == 0) printf("0x%04llx:", page * page_size + i); printf(" %02x", buf[i]); if (i % 16 == 15) printf("\n"); } /* Dump OOB data */ printf("Address 0x%llx, (Block 0x%llx, page 0x%llx) OOB:\n", page * page_size, page / pages_per_block, page % pages_per_block); for (i = 0; i < oob_size; i++) { if (i % 16 == 0) printf("0x%04x:", i); printf(" %02x", buf[page_size + i]); if (i % 16 == 15) printf("\n"); } printf("\n"); page++; } return 0; } else if (!strcmp("erase", cmd)) { u64 start_addr, length; int force = 0; u64 i; int page_size = cvmx_nand_get_page_size(chip); int pages_per_block = cvmx_nand_get_pages_per_block(chip); if (argc < 3 || argc > 5) goto usage; start_addr = simple_strtoull(argv[2], NULL, 16); if (argc >= 4) length = simple_strtoull(argv[3], NULL, 16); else length = page_size * pages_per_block; if (argc == 5) { if (strcmp(argv[4], "force")) goto usage; printf("WARNING: Forced erase: erasing bad blocks.\n"); force = 1; } if (!page_size) { printf("ERROR: No NAND configured (run probe)\n"); return 1; } if (start_addr & ((u64) page_size * pages_per_block - 1)) { printf("ERROR: erase start not at block boundary " "(maybe want: 0x%llx or 0x%llx)\n", start_addr & ~((u64) page_size * pages_per_block - 1), (start_addr + page_size * pages_per_block) & ~((u64) page_size * pages_per_block - 1)); return 1; } if ((length & (page_size * pages_per_block - 1))) { printf ("ERROR: erase length not multiple of block size " "(maybe want: 0x%llx)\n", (length + (page_size * pages_per_block - 1)) & ~(page_size * pages_per_block - 1)); return 1; } for (i = start_addr; i < start_addr + length; i += page_size * pages_per_block) { if (oct_nand_block_is_bad(chip, i, 1)) { if (force) { printf("WARNING: Erasing bad bock at " "address 0x%llx\n", i); } else { printf("Not erasing bad block at " "address 0x%llx\n", i); continue; } } if (cvmx_nand_block_erase(chip, i)) { printf("cvmx_nand_block_erase() failed, " "addr 0x%08llx\n", i); return 1; } } return 0; } else if (!strcmp("write", cmd)) { u64 nand_addr; u32 dram_addr, length; int page_size = cvmx_nand_get_page_size(chip); if (!page_size) { printf("ERROR: No NAND configured (run probe)\n"); return 1; } if (argc != 3 && argc != 5) goto usage; nand_addr = simple_strtoull(argv[2], NULL, 16); if (argc == 5) { dram_addr = simple_strtoul(argv[3], NULL, 16); length = simple_strtoul(argv[4], NULL, 16); } else { /* We look up the length/address from environment * variables.... * filesize=3410 * fileaddr=20000000 */ if (getenv("filesize") && getenv("fileaddr")) { dram_addr = simple_strtoul(getenv("fileaddr"), NULL, 16); length = simple_strtoul(getenv("filesize"), NULL, 16); /* Round length up to pagesize */ length = (length + page_size - 1) & ~(page_size - 1); } else { printf("ERROR: filesize and fileaddr " "environment variables\n" "must be set if address and size " "are not given\n"); goto usage; } } unsigned char *data_ptr = (void *)dram_addr; if (nand_addr & (page_size - 1)) { printf("ERROR: write address not at page boundary " "(maybe want: 0x%08llx)\n", nand_addr & ~(page_size - 1)); return 1; } return oct_nand_boot_write(nand_addr, data_ptr, length, 0); } else if (!strcmp("scan", cmd)) { u64 nand_addr; bootloader_header_t header_copy; int page_size = cvmx_nand_get_page_size(chip); int pages_per_block = cvmx_nand_get_pages_per_block(chip); int block_size = page_size * pages_per_block; int errors = 0; int read_size = CVMX_NAND_BOOT_ECC_BLOCK_SIZE + CVMX_NAND_BOOT_ECC_ECC_SIZE; if (!page_size) { printf("ERROR: No NAND configured (run probe)\n"); return 1; } printf("Scanning NAND for recognized images\n"); for (nand_addr = 0; nand_addr < MAX_NAND_SEARCH_ADDR; nand_addr += page_size) { /* Adjust the read address based on location in page */ int offset = ((nand_addr & (page_size - 1)) / CVMX_NAND_BOOT_ECC_BLOCK_SIZE) * CVMX_NAND_BOOT_ECC_ECC_SIZE; int bytes = cvmx_nand_page_read(chip, nand_addr + offset, cvmx_ptr_to_phys(buf), read_size); if (bytes != read_size) { printf("Error reading NAND addr 0x%08llx " "(bytes_read: %d, expected: %d)\n", nand_addr, bytes, read_size); return 1; } errors = cvmx_nand_correct_boot_ecc(buf); if (errors <= 1) { const bootloader_header_t *header = (const bootloader_header_t *)buf; /* Block is good, see if it contains a * bootloader image header */ if ((header->magic == BOOTLOADER_HEADER_MAGIC)) { int rval; /* Check the CRC of the header */ u32 crc; crc = crc32(0, (void *)header, 12); crc = crc32(crc, (void *)header + 16, header->hlen - 16); if (crc != header->hcrc) { printf("ERROR: 0x%llx Header " "CRC mismatch, expected:" " 0x%08x, found:" " 0x%08x\n", nand_addr, header->hcrc, crc); } else { header_copy = *header; printf("\nImage header at addr:" "0x%llx, size: 0x%x, " "crc: 0x%x ", nand_addr, header->dlen, header->dcrc); if ((rval = oct_nand_validate_image(nand_addr)) < 0) { printf("Invalid image(%d).\n", rval); } else { printf ("Valid image.\n"); if (header_copy.flags & BL_HEADER_FLAG_FAILSAFE) printf("Failsafe"); else printf("Normal"); if (header_copy.image_type == BL_HEADER_IMAGE_STAGE2) printf("NAND stage2 "); else if (header_copy.image_type == BL_HEADER_IMAGE_STAGE3) printf("NAND stage3 "); else if (header_copy.image_type == BL_HEADER_IMAGE_UBOOT_ENV) printf("U-boot environment (version: %d)\n", (int)header_copy.address); if (header_copy.board_type) printf("image for %s board\n", cvmx_board_type_to_string(header_copy.board_type)); else printf("\n"); /* Skip the image, as we * have validated it */ nand_addr += ((header->dlen + page_size - 1) & ~(page_size - 1)) - page_size; } } } } else { /* Uncorrectable errors detected. */ /* Check to see if we have a bad block */ if (oct_nand_block_is_bad(chip, nand_addr, 1)) { printf ("Bad NAND block at addr: 0x%llx\n", nand_addr & ~(block_size - 1)); /* Skip rest of block */ nand_addr += block_size - (nand_addr & (block_size - 1)) - page_size; } } } printf("\n"); return 0; } usage: printf("Usage:\n%s\n", cmdtp->usage); return 1; }