Example #1
0
int flash_register(struct blocklevel_device *bl)
{
	uint64_t size;
	uint32_t block_size;
	struct ffs_handle *ffs;
	struct dt_node *node;
	struct flash *flash;
	const char *name;
	int rc;

	rc = blocklevel_get_info(bl, &name, &size, &block_size);
	if (rc)
		return rc;

	if (!name)
		name = "(unnamed)";

	prlog(PR_INFO, "FLASH: registering flash device %s "
			"(size 0x%llx, blocksize 0x%x)\n",
			name, size, block_size);

	flash = malloc(sizeof(struct flash));
	if (!flash) {
		prlog(PR_ERR, "FLASH: Error allocating flash structure\n");
		return OPAL_RESOURCE;
	}

	flash->busy = false;
	flash->bl = bl;
	flash->no_erase = !(bl->flags & WRITE_NEED_ERASE);
	flash->size = size;
	flash->block_size = block_size;
	flash->id = num_flashes();

	rc = ffs_init(0, flash->size, bl, &ffs, 1);
	if (rc) {
		/**
		 * @fwts-label NoFFS
		 * @fwts-advice System flash isn't formatted as expected.
		 * This could mean several OPAL utilities do not function
		 * as expected. e.g. gard, pflash.
		 */
		prlog(PR_WARNING, "FLASH: No ffs info; "
				"using raw device only\n");
		ffs = NULL;
	}

	node = flash_add_dt_node(flash, flash->id);

	setup_system_flash(flash, node, name, ffs);

	if (ffs)
		ffs_close(ffs);

	lock(&flash_lock);
	list_add(&flashes, &flash->list);
	unlock(&flash_lock);

	return OPAL_SUCCESS;
}
Example #2
0
int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len)
{
	uint32_t erase_size;
	const void *write_buf = buf;
	void *write_buf_start = NULL;
	uint64_t ecc_start;
	void *erase_buf;
	int rc = 0;

	if (!write_buf || !bl) {
		errno = EINVAL;
		return FLASH_ERR_PARM_ERROR;
	}

	FL_DBG("%s: 0x%" PRIx64 "\t0x%" PRIx64 "\n", __func__, pos, len);

	if (!(bl->flags & WRITE_NEED_ERASE)) {
		FL_DBG("%s: backend doesn't need erase\n", __func__);
		return blocklevel_write(bl, pos, buf, len);
	}

	rc = blocklevel_get_info(bl, NULL, NULL, &erase_size);
	if (rc)
		return rc;

	if (ecc_protected(bl, pos, len, &ecc_start)) {
		FL_DBG("%s: region has ECC\n", __func__);

		len = ecc_buffer_size(len);

		write_buf_start = malloc(len);
		if (!write_buf_start) {
			errno = ENOMEM;
			return FLASH_ERR_MALLOC_FAILED;
		}

		if (memcpy_to_ecc(write_buf_start, buf, ecc_buffer_size_minus_ecc(len))) {
			free(write_buf_start);
			errno = EBADF;
			return FLASH_ERR_ECC_INVALID;
		}
		write_buf = write_buf_start;
	}

	erase_buf = malloc(erase_size);
	if (!erase_buf) {
		errno = ENOMEM;
		rc = FLASH_ERR_MALLOC_FAILED;
		goto out_free;
	}

	rc = reacquire(bl);
	if (rc)
		goto out_free;

	while (len > 0) {
		uint32_t erase_block = pos & ~(erase_size - 1);
		uint32_t block_offset = pos & (erase_size - 1);
		uint32_t size = erase_size > len ? len : erase_size;
		int cmp;

		/* Write crosses an erase boundary, shrink the write to the boundary */
		if (erase_size < block_offset + size) {
			size = erase_size - block_offset;
		}

		rc = bl->read(bl, erase_block, erase_buf, erase_size);
		if (rc)
			goto out;

		cmp = blocklevel_flashcmp(erase_buf + block_offset, write_buf, size);
		FL_DBG("%s: region 0x%08x..0x%08x ", __func__,
				erase_block, erase_size);
		if (cmp != 0) {
			FL_DBG("needs ");
			if (cmp == -1) {
				FL_DBG("erase and ");
				bl->erase(bl, erase_block, erase_size);
			}
			FL_DBG("write\n");
			memcpy(erase_buf + block_offset, write_buf, size);
			rc = bl->write(bl, erase_block, erase_buf, erase_size);
			if (rc)
				goto out;
		}
		len -= size;
		pos += size;
		write_buf += size;
	}

out:
	release(bl);
out_free:
	free(write_buf_start);
	free(erase_buf);
	return rc;
}
Example #3
0
int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len)
{
	uint32_t erase_size;
	const void *write_buf = buf;
	void *write_buf_start = NULL;
	void *erase_buf;
	int rc = 0;

	if (!write_buf || !bl) {
		errno = EINVAL;
		return FLASH_ERR_PARM_ERROR;
	}

	if (!(bl->flags & WRITE_NEED_ERASE))
		return blocklevel_write(bl, pos, buf, len);

	rc = blocklevel_get_info(bl, NULL, NULL, &erase_size);
	if (rc)
		return rc;

	if (ecc_protected(bl, pos, len)) {
		len = ecc_buffer_size(len);

		write_buf_start = malloc(len);
		if (!write_buf_start) {
			errno = ENOMEM;
			return FLASH_ERR_MALLOC_FAILED;
		}

		if (memcpy_to_ecc(write_buf_start, buf, ecc_buffer_size_minus_ecc(len))) {
			free(write_buf_start);
			errno = EBADF;
			return FLASH_ERR_ECC_INVALID;
		}
		write_buf = write_buf_start;
	}

	erase_buf = malloc(erase_size);
	if (!erase_buf) {
		errno = ENOMEM;
		rc = FLASH_ERR_MALLOC_FAILED;
		goto out;
	}

	while (len > 0) {
		uint32_t erase_block = pos & ~(erase_size - 1);
		uint32_t block_offset = pos & (erase_size - 1);
		uint32_t size = erase_size > len ? len : erase_size;
		int cmp;

		/* Write crosses an erase boundary, shrink the write to the boundary */
		if (erase_size < block_offset + size) {
			size = erase_size - block_offset;
		}

		rc = bl->read(bl, erase_block, erase_buf, erase_size);
		if (rc)
			goto out;

		cmp = blocklevel_flashcmp(erase_buf + block_offset, write_buf, size);
		if (cmp != 0) {
			if (cmp == -1)
				bl->erase(bl, erase_block, erase_size);
			memcpy(erase_buf + block_offset, write_buf, size);
			rc = bl->write(bl, erase_block, erase_buf, erase_size);
			if (rc)
				goto out;
		}
		len -= size;
		pos += size;
		write_buf += size;
	}

out:
	free(write_buf_start);
	free(erase_buf);
	return rc;
}
Example #4
0
int main(int argc, char *argv[])
{
    const char *pname = argv[0];
    uint32_t address = 0, read_size = 0, write_size = 0;
    uint32_t erase_start = 0, erase_size = 0;
    bool erase = false, do_clear = false;
    bool program = false, erase_all = false, info = false, do_read = false;
    bool enable_4B = false, disable_4B = false;
    bool show_help = false, show_version = false;
    bool no_action = false, tune = false;
    char *write_file = NULL, *read_file = NULL, *part_name = NULL;
    bool ffs_toc_seen = false;
    int rc;

    while(1) {
        static struct option long_opts[] = {
            {"address",	required_argument,	NULL,	'a'},
            {"size",	required_argument,	NULL,	's'},
            {"partition",	required_argument,	NULL,	'P'},
            {"bmc",		no_argument,		NULL,	'b'},
            {"enable-4B",	no_argument,		NULL,	'4'},
            {"disable-4B",	no_argument,		NULL,	'3'},
            {"read",	required_argument,	NULL,	'r'},
            {"erase-all",	no_argument,		NULL,	'E'},
            {"erase",	no_argument,		NULL,	'e'},
            {"program",	required_argument,	NULL,	'p'},
            {"force",	no_argument,		NULL,	'f'},
            {"info",	no_argument,		NULL,	'i'},
            {"tune",	no_argument,		NULL,	't'},
            {"dummy",	no_argument,		NULL,	'd'},
            {"help",	no_argument,		NULL,	'h'},
            {"version",	no_argument,		NULL,	'v'},
            {"debug",	no_argument,		NULL,	'g'},
            {"side",	required_argument,	NULL,	'S'},
            {"toc",		required_argument,	NULL,	'T'},
            {"clear",   no_argument,        NULL,   'c'}
        };
        int c, oidx = 0;

        c = getopt_long(argc, argv, "a:s:P:r:43Eep:fdihvbtgS:T:c",
                        long_opts, &oidx);
        if (c == EOF)
            break;
        switch(c) {
        case 'a':
            address = strtoul(optarg, NULL, 0);
            break;
        case 's':
            read_size = write_size = strtoul(optarg, NULL, 0);
            break;
        case 'P':
            part_name = strdup(optarg);
            break;
        case '4':
            enable_4B = true;
            break;
        case '3':
            disable_4B = true;
            break;
        case 'r':
            do_read = true;
            read_file = strdup(optarg);
            break;
        case 'E':
            erase_all = erase = true;
            break;
        case 'e':
            erase = true;
            break;
        case 'p':
            program = true;
            write_file = strdup(optarg);
            break;
        case 'f':
            must_confirm = false;
            break;
        case 'd':
            must_confirm = false;
            dummy_run = true;
            break;
        case 'i':
            info = true;
            break;
        case 'b':
            bmc_flash = true;
            break;
        case 't':
            tune = true;
            break;
        case 'v':
            show_version = true;
            break;
        case 'h':
            show_help = show_version = true;
            break;
        case 'g':
            libflash_debug = true;
            break;
        case 'S':
            flash_side = atoi(optarg);
            break;
        case 'T':
            ffs_toc_seen = true;
            ffs_toc = strtoul(optarg, NULL, 0);
            break;
        case 'c':
            do_clear = true;
            break;
        default:
            exit(1);
        }
    }

    /* Check if we need to access the flash at all (which will
     * also tune them as a side effect
     */
    no_action = !erase && !program && !info && !do_read &&
                !enable_4B && !disable_4B && !tune && !do_clear;

    /* Nothing to do, if we didn't already, print usage */
    if (no_action && !show_version)
        show_help = show_version = true;

    if (show_version)
        print_version();
    if (show_help)
        print_help(pname);

    if (no_action)
        return 0;

    /* --enable-4B and --disable-4B are mutually exclusive */
    if (enable_4B && disable_4B) {
        fprintf(stderr, "--enable-4B and --disable-4B are mutually"
                " exclusive !\n");
        exit(1);
    }

    /* 4B not supported on BMC flash */
    if (enable_4B && bmc_flash) {
        fprintf(stderr, "--enable-4B not supported on BMC flash !\n");
        exit(1);
    }

    /* partitions not supported on BMC flash */
    if (part_name && bmc_flash) {
        fprintf(stderr, "--partition not supported on BMC flash !\n");
        exit(1);
    }

    /* part-name and erase-all make no sense together */
    if (part_name && erase_all) {
        fprintf(stderr, "--partition and --erase-all are mutually"
                " exclusive !\n");
        exit(1);
    }

    /* Read command should always come with a file */
    if (do_read && !read_file) {
        fprintf(stderr, "Read with no file specified !\n");
        exit(1);
    }

    /* Program command should always come with a file */
    if (program && !write_file) {
        fprintf(stderr, "Program with no file specified !\n");
        exit(1);
    }

    /* If both partition and address specified, error out */
    if (address && part_name) {
        fprintf(stderr, "Specify partition or address, not both !\n");
        exit(1);
    }

    if (do_clear && !part_name) {
        fprintf(stderr, "--clear only supported on a partition name\n");
        exit(1);
    }

    /* Explicitly only support two sides */
    if (flash_side != 0 && flash_side != 1) {
        fprintf(stderr, "Unexpected value for --side '%d'\n", flash_side);
        exit(1);
    }

    if (ffs_toc_seen && flash_side) {
        fprintf(stderr, "--toc and --side are exclusive");
        exit(1);
    }

    /* If file specified but not size, get size from file
     */
    if (write_file && !write_size) {
        struct stat stbuf;

        if (stat(write_file, &stbuf)) {
            perror("Failed to get file size");
            exit(1);
        }
        write_size = stbuf.st_size;
    }

    if (bmc_flash) {
        if (arch_flash_bmc(NULL, 1) == -1) {
            fprintf(stderr, "Can't switch to BMC flash on this architecture\n");
            exit(1);
        }
    }

    if (arch_flash_init(&bl, NULL, true))
        exit(1);

    on_exit(exiting, NULL);


    rc = blocklevel_get_info(bl, &fl_name,
                             &fl_total_size, &fl_erase_granule);
    if (rc) {
        fprintf(stderr, "Error %d getting flash info\n", rc);
        exit(1);
    }

    /* If -t is passed, then print a nice message */
    if (tune)
        printf("Flash and controller tuned\n");

    /* If read specified and no read_size, use flash size */
    if (do_read && !read_size && !part_name)
        read_size = fl_total_size;

    /* We have a partition specified, grab the details */
    if (part_name)
        lookup_partition(part_name);

    /* We have a partition, adjust read/write size if needed */
    if (ffsh && ffs_index >= 0) {
        uint32_t pstart, pmaxsz, pactsize;
        bool ecc;
        int rc;

        rc = ffs_part_info(ffsh, ffs_index, NULL,
                           &pstart, &pmaxsz, &pactsize, &ecc);
        if (rc) {
            fprintf(stderr,"Failed to get partition info\n");
            exit(1);
        }

        if (!ecc && do_clear) {
            fprintf(stderr, "The partition on which to do --clear "
                    "does not have ECC, are you sure?\n");
            check_confirm();
            /* Still confirm later on */
            must_confirm = true;
        }

        /* Read size is obtained from partition "actual" size */
        if (!read_size)
            read_size = pactsize;

        /* Write size is max size of partition */
        if (!write_size)
            write_size = pmaxsz;

        /* Crop write size to partition size */
        if (write_size > pmaxsz) {
            printf("WARNING: Size (%d bytes) larger than partition"
                   " (%d bytes), cropping to fit\n",
                   write_size, pmaxsz);
            write_size = pmaxsz;
        }

        /* If erasing, check partition alignment */
        if (erase && ((pstart | pmaxsz) & 0xfff)) {
            fprintf(stderr,"Partition not aligned properly\n");
            exit(1);
        }

        /* Set address */
        address = pstart;
    }

    /* Align erase boundaries */
    if (erase && !erase_all) {
        uint32_t mask = 0xfff;
        uint32_t erase_end;

        /* Dummy size for erase, will be adjusted later */
        if (!write_size)
            write_size = 1;
        erase_start = address & ~mask;
        erase_end = ((address + write_size) + mask) & ~mask;
        erase_size = erase_end - erase_start;

        if (erase_start != address || erase_size != write_size)
            fprintf(stderr, "WARNING: Erase region adjusted"
                    " to 0x%08x..0x%08x\n",
                    erase_start, erase_end);
    }

    /* Process commands */
    if (enable_4B)
        enable_4B_addresses();
    if (disable_4B)
        disable_4B_addresses();
    if (info) {
        /*
         * Don't pass through modfied TOC value if the modification was done
         * because of --size, but still respect if it came from --toc (we
         * assume the user knows what they're doing in that case)
         */
        print_flash_info(flash_side ? 0 : ffs_toc);
    }

    /* Unlock flash (PNOR only) */
    if ((erase || program || do_clear) && !bmc_flash) {
        need_relock = arch_flash_set_wrprotect(bl, false);
        if (need_relock == -1) {
            fprintf(stderr, "Architecture doesn't support write protection on flash\n");
            need_relock = 0;
            exit (1);
        }
    }
    if (do_read)
        do_read_file(read_file, address, read_size);
    if (erase_all)
        erase_chip();
    else if (erase)
        erase_range(erase_start, erase_size, program);
    if (program)
        program_file(write_file, address, write_size);
    if (do_clear)
        set_ecc(address, write_size);
    return 0;
}
Example #5
0
int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl,
		struct ffs_handle **ffs, bool mark_ecc)
{
	struct ffs_hdr hdr;
	struct ffs_hdr blank_hdr;
	struct ffs_handle *f;
	uint64_t total_size;
	int rc, i;

	if (!ffs || !bl)
		return FLASH_ERR_PARM_ERROR;
	*ffs = NULL;

	rc = blocklevel_get_info(bl, NULL, &total_size, NULL);
	if (rc) {
		FL_ERR("FFS: Error %d retrieving flash info\n", rc);
		return rc;
	}
	if (total_size > UINT_MAX)
		return FLASH_ERR_VERIFY_FAILURE;
	if ((offset + max_size) < offset)
		return FLASH_ERR_PARM_ERROR;

	if ((max_size > total_size))
		return FLASH_ERR_PARM_ERROR;

	/* Read flash header */
	rc = blocklevel_read(bl, offset, &hdr, sizeof(hdr));
	if (rc) {
		FL_ERR("FFS: Error %d reading flash header\n", rc);
		return rc;
	}

	/*
	 * Flash controllers can get deconfigured or otherwise upset, when this
	 * happens they return all 0xFF bytes.
	 * An ffs_hdr consisting of all 0xFF cannot be valid and it would be
	 * nice to drop a hint to the user to help with debugging. This will
	 * help quickly differentiate between flash corruption and standard
	 * type 'reading from the wrong place' errors vs controller errors or
	 * reading erased data.
	 */
	memset(&blank_hdr, UINT_MAX, sizeof(struct ffs_hdr));
	if (memcmp(&blank_hdr, &hdr, sizeof(struct ffs_hdr)) == 0) {
		FL_ERR("FFS: Reading the flash has returned all 0xFF.\n");
		FL_ERR("Are you reading erased flash?\n");
		FL_ERR("Is something else using the flash controller?\n");
		return FLASH_ERR_BAD_READ;
	}

	/* Allocate ffs_handle structure and start populating */
	f = malloc(sizeof(*f));
	if (!f)
		return FLASH_ERR_MALLOC_FAILED;
	memset(f, 0, sizeof(*f));

	f->toc_offset = offset;
	f->max_size = max_size;
	f->bl = bl;

	/* Convert and check flash header */
	rc = ffs_check_convert_header(&f->hdr, &hdr);
	if (rc) {
		FL_INF("FFS: Flash header not found. Code: %d\n", rc);
		goto out;
	}

	/* Check header is sane */
	if ((f->hdr.block_size * f->hdr.size) > max_size) {
		rc = FLASH_ERR_PARM_ERROR;
		FL_ERR("FFS: Flash header exceeds max flash size\n");
		goto out;
	}

	if ((f->hdr.entry_size * f->hdr.entry_count) >
			(f->hdr.block_size * f->hdr.size)) {
		rc = FLASH_ERR_PARM_ERROR;
		FL_ERR("FFS: Flash header entries exceeds available blocks\n");
		goto out;
	}

	/*
	 * Decide how much of the image to grab to get the whole
	 * partition map.
	 */
	f->cached_size = f->hdr.block_size * f->hdr.size;
	/* Check for overflow or a silly size */
	if (!f->hdr.size || f->cached_size / f->hdr.size != f->hdr.block_size) {
		rc= FLASH_ERR_MALLOC_FAILED;
		FL_ERR("FFS: Cache size overflow (0x%x * 0x%x)\n",
				f->hdr.block_size, f->hdr.size);
		goto out;
	}

	FL_DBG("FFS: Partition map size: 0x%x\n", f->cached_size);

	/* Allocate cache */
	f->cache = malloc(f->cached_size);
	if (!f->cache) {
		rc = FLASH_ERR_MALLOC_FAILED;
		goto out;
	}

	/* Read the cached map */
	rc = blocklevel_read(bl, offset, f->cache, f->cached_size);
	if (rc) {
		FL_ERR("FFS: Error %d reading flash partition map\n", rc);
		goto out;
	}

	if (mark_ecc) {
		uint32_t start, total_size;
		bool ecc;
		for (i = 0; i < f->hdr.entry_count; i++) {
			rc = ffs_part_info(f, i, NULL, &start, &total_size,
					NULL, &ecc);
			if (rc) {
				FL_ERR("FFS: Failed to read ffs partition %d\n",
						i);
				goto out;
			}
			if (ecc) {
				rc = blocklevel_ecc_protect(bl, start, total_size);
				if (rc) {
					FL_ERR("FFS: Failed to blocklevel_ecc_protect(0x%08x, 0x%08x)\n",
					       start, total_size);
					goto out;
				}
			}  /* ecc */
		} /* for */
	}

out:
	if (rc == 0)
		*ffs = f;
	else
		free(f);

	return rc;
}