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