/**
 * 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;
}
Exemple #2
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;
}