Esempio n. 1
0
/* Checks to see if the page (including OOB) at given address is blank
 * returns: 1 if blank
 *          0 if !blank
 */
int oct_nand_page_is_blank(u64 nand_addr)
{
    unsigned char *buf = (unsigned char *)buf_storage;
    int page_size = cvmx_nand_get_page_size(chip);
    int read_size = page_size + octeon_nand_get_oob_size(chip);
    int bytes;
    int i;

    if (nand_addr & (page_size - 1))
        printf("WARNING: non page aligned address passed to %s\n",
               __FUNCTION__);

    nand_addr &= ~(page_size - 1);

    /* Check to see if the page/block is bad.
     * Treat bad blocks as non-blank, so they won't get written to
     */
    if (oct_nand_block_is_bad(chip, nand_addr, 1)) {
        printf("page at addr 0x%llx is bad, returning !blank\n",
               nand_addr);
        return 0;
    }

    bytes = cvmx_nand_page_read(chip, nand_addr, cvmx_ptr_to_phys(buf),
                                read_size);
    if (bytes != read_size) {
        printf("ERROR: %s: error reading NAND.\n", __FUNCTION__);
        return 0;
    }
    for (i = 0; i < read_size; i++) {
        if (buf[i] != 0xff)
            return (0);
    }
    return 1;
}
/**
 * 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;
}
Esempio n. 3
0
/* Writes to NAND using Octeon NAND-boot ECC */
oct_nand_status_t oct_nand_boot_write(u64 nand_addr, void *data, int len,
                                      u32 flags)
{
    u64 tmp_addr;
    int input_remaining;
    u64 buf_storage[CVMX_NAND_MAX_PAGE_AND_OOB_SIZE / 8];
    unsigned char *buf = (unsigned char *)buf_storage;
    int page_size = cvmx_nand_get_page_size(chip);
    int oob_size = octeon_nand_get_oob_size(chip);

    if (nand_addr & (page_size - 1)) {
        printf("ERROR: write start must be page aligned.\n");
        return OCT_NAND_BAD_PARAM;
    }

    /* Round length up to page size */
    len = (len + page_size - 1) & ~(page_size - 1);

    /* First check to make sure all pages are erased.  We don't count the
     * OOB data against the length supplied, as that is where the ECC data
     * will go.  BAD blocks are returned as !blank, so we will also skip
     * bad blocks
     */
    for (tmp_addr = nand_addr; tmp_addr < nand_addr + len;
            tmp_addr += page_size) {
        if (oct_nand_block_is_bad(chip, tmp_addr, 1)) {
            printf("ERROR: Block containing addr 0x%llx is bad, "
                   "unable to write to page\n", tmp_addr);
            return OCT_NAND_ERROR;
        }
        if (!oct_nand_page_is_blank(tmp_addr)) {
            printf("ERROR: page at addr 0x%llx is not blank, "
                   "unable to write to page\n", tmp_addr);
            return OCT_NAND_ERROR;
        }
    }

    /* Now we have verified that the required flash is erased (and not bad),
     * now generate the ECC and write the data to the flash.
     */
    int offset;		/* offset within NAND page while computing ECC
				 * (may limit size of final write)
				 */
    input_remaining = len;
    for (tmp_addr = nand_addr; tmp_addr < nand_addr + len;
            tmp_addr += page_size) {
        u64 buf1_storage[CVMX_NAND_MAX_PAGE_AND_OOB_SIZE / 8];
        unsigned char *buf1 = (unsigned char *)buf1_storage;

        memset(buf, 0, sizeof(buf));
        offset = 0;
        while (input_remaining > 0 &&
                offset <= (page_size + oob_size -
                           CVMX_NAND_BOOT_ECC_BLOCK_SIZE -
                           CVMX_NAND_BOOT_ECC_ECC_SIZE)) {
            memcpy(buf + offset, data,
                   MIN(CVMX_NAND_BOOT_ECC_BLOCK_SIZE,
                       input_remaining));
            cvmx_nand_compute_boot_ecc(buf + offset, buf + offset +
                                       CVMX_NAND_BOOT_ECC_BLOCK_SIZE);
            offset += CVMX_NAND_BOOT_ECC_BLOCK_SIZE +
                      CVMX_NAND_BOOT_ECC_ECC_SIZE;
            data += CVMX_NAND_BOOT_ECC_BLOCK_SIZE;
            input_remaining -= CVMX_NAND_BOOT_ECC_BLOCK_SIZE;
        }
        /* We have filled up a page, so write it */
        if (cvmx_nand_page_write(chip, tmp_addr, cvmx_ptr_to_phys(buf))) {
            printf("cvmx_nand_page_write() failed, address "
                   "0x%08llx\n", tmp_addr);
            return OCT_NAND_WRITE_ERROR;
        }
        /* Now read the block to make sure it matches */
        if (cvmx_nand_page_read(chip, tmp_addr,
                                cvmx_ptr_to_phys(buf1), offset)
                != offset) {
            printf("ERROR reading back NAND to verify write.\n");
            return OCT_NAND_READ_ERROR;
        }
        if (memcmp(buf, buf1, offset)) {
            printf("ERROR: write verify failed.\n");
            return OCT_NAND_WRITE_ERROR;
        }
    }
    return OCT_NAND_SUCCESS;
}
Esempio n. 4
0
/**
 * Checks to see if address is part of a bad block.
 * This does the 'standard' bad block checks, but also takes
 * into account the Octeon NAND boot ECC format.  Most blocks
 * with this format will appear to be bad, so we do extra checks
 * for blocks that fail the standard bad block test.  If a 'bad' block
 * passes Octeon ECC, then it is treated as good.
 * One complication is that some bad blocks are all 0x00 bytes, which
 * also happens to be a valid Octeon ECC encoding.  This special case is
 * checked for, and treated as a bad block.
 *
 * @param chip_select
 *                  Chip select for operation
 * @param nand_addr Address of block to check.  (Any address within block is
 *                  OK.)
 * @param check_octeon_ecc
 *                  Do extra checks for Octeon ECC.  Without these checks, many
 *                  Octeon NAND boot blocks will be reported as bad.
 *
 * @return 1 if bad
 *         0 if OK
 */
int oct_nand_block_is_bad(int chip_select, uint64_t nand_addr,
                          int check_octeon_ecc)
{
    /* Cache the results of the most recent check,
     * so that we can answer quickly about all pages in
     * a block.
     */
    static u64 cached_block_addr = ~0ull;
    static int cached_block_is_bad = 0;

    unsigned char *buf = (unsigned char *)buf_storage;
    int page_size = cvmx_nand_get_page_size(chip_select);
    int pages_per_block = cvmx_nand_get_pages_per_block(chip_select);
    int block_size = page_size * pages_per_block;
    int read_size = page_size + octeon_nand_get_oob_size(chip_select);
    int block_is_bad = 0;
    int bytes;
    int i;
    int page_to_check;
    u64 nand_addr_read;

    /* Set the bad block position */
    int bad_block_pos = page_size + (page_size > 512 ?
                                     NAND_LARGE_BADBLOCK_POS :
                                     NAND_SMALL_BADBLOCK_POS);

    /* Since NAND is marked bad in terms of blocks, we only
     * care about what block the supplied address is in
     */
    nand_addr &= ~(block_size - 1);

    /* Return cached value if we have a match */
    if (nand_addr == cached_block_addr)
        return cached_block_is_bad;

    nand_addr_read = nand_addr;
    for (page_to_check = 0; page_to_check < 2; page_to_check++) {
        /* Check first and last pages of block */
        bytes = cvmx_nand_page_read(chip_select, nand_addr_read,
                                    cvmx_ptr_to_phys(buf), read_size);
        if (bytes != read_size) {
            printf("ERROR: %s: error reading NAND.\n",
                   __FUNCTION__);
            return 1;
        }
        if (buf[bad_block_pos] != 0xFF)
            block_is_bad = 1;

        if (block_is_bad) {
            if (check_octeon_ecc) {
                /* Check to see if there is a valid Octeon ECC
                 * encoded block at the beginning of the page.
                 * If so, the block is good, otherwise it is
                 * bad
                 */
                if (!cvmx_nand_correct_boot_ecc(buf)) {
                    /* No ECC errors detected */
                    /* Some bad blocks are set to all 0x00,
                     * including OOB.  This meets all bad
                     * block marking requirements, but is
                     * also a valid Octeon BOOT bus
                     * encoding.  Treat this as a bad block.
                     */
                    for (i = 0; i < read_size; i++)
                        if (buf[i])
                            break;
                    if (i == read_size) {
                        block_is_bad = 1;
                        goto page_is_bad_exit;
                    }
                    block_is_bad = 0;
                }
            } else
                goto page_is_bad_exit;
        }
        nand_addr_read += block_size - page_size;
    }

page_is_bad_exit:
    /* Cache most recent lookup so looking up pages in same block is fast */
    cached_block_addr = nand_addr;
    cached_block_is_bad = block_is_bad;
    return block_is_bad;
}