/**
 * 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;
}
Beispiel #2
0
/* Look for an image of a given type in the NAND address range given.  If an
 * image type of 0 is passed, matches all valid images
 */
u64 oct_nand_image_search(u64 start_addr, u64 max_addr, u16 image_type)
{
    unsigned char *buf = (unsigned char *)buf_storage;

    int page_size;
    int chip = oct_nand_get_cur_chip();
    u64 nand_addr;
    bootloader_header_t header_copy;

    if (chip < 0) {
        special_debug_printf("No nand chips found\n");
        return 0;
    }

    page_size = cvmx_nand_get_page_size(chip);

    /* Round start address up to a page boundary */
    start_addr = (start_addr + page_size - 1) & ~(page_size - 1);

    special_debug_printf("Using NAND chip %d, page size: %d\n", chip,
                         page_size);
    int errors = 0;
    int read_size = CVMX_NAND_BOOT_ECC_BLOCK_SIZE +
                    CVMX_NAND_BOOT_ECC_ECC_SIZE;
    if (!page_size) {
        special_debug_printf("ERROR: No NAND configured (run probe)\n");
        return 0;
    }
    special_debug_printf("Scanning NAND for for image type: %d\n",
                         image_type);
    for (nand_addr = start_addr; nand_addr < max_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) {
            special_debug_printf("Error reading NAND addr %d "
                                 "(bytes_read: %d, expected: %d)\n",
                                 nand_addr, bytes, read_size);
            return -1;
        }
        errors = cvmx_nand_correct_boot_ecc(buf);
        if (errors <= 1) {
            if (errors == 1)
                special_debug_printf("Correctable ECC error at "
                                     "NAND address: 0x%llx\n",
                                     nand_addr + offset);

            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)) {
                special_debug_printf("\nFound a header at addr 0x%llx\n",
                                     nand_addr);

                /* 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) {
                    /* Copy header, as validate_image() call
                     * will overwrite global buffer
                     */
                    header_copy = *header;
                    if (oct_nand_validate_image(nand_addr)
                            >= 0) {
                        if (!image_type
                                || header_copy.image_type == image_type) {
                            /* We found a valid
                             * image of the type we
                             * were looking for, so
                             * return the address of it.
                             */
                            special_debug_printf("Image type match at addr 0x%llx\n",
                                                 nand_addr);
                            return nand_addr;
                        }
                        /* Skip the image, as we have
                         * validated it
                         */
                        special_debug_printf("\nSkipping image type: %d\n",
                                             header_copy.image_type);
                        nand_addr +=
                            ((header_copy.hlen +
                              header_copy.dlen +
                              page_size -
                              1) & ~(page_size - 1)) -
                            page_size;
                    } else {
                        special_debug_printf("Image CRC failed\n");
                    }
                } else {
                    special_debug_printf("Header CRC failed\n");
                }
            }
        } else {
            special_debug_printf("Uncorrectable ECC error at NAND "
                                 "address: 0x%llx\n",
                                 nand_addr + offset);
        }
    }

    special_debug_printf("Done looking for image type: %d, none found.\n",
                         image_type);
    return 0;

}