Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02f8a24e authored by Aaron Wu's avatar Aaron Wu Committed by Brian Norris
Browse files

mtd: gpio_flash: handle case where offset + len exceeds the window size



Fix the bug in handling gpio flash read/write when offset + len
from MTD exceeds the window size

Signed-off-by: default avatarAaron Wu <Aaron.wu@analog.com>

[Brian: made some commentary edits. Also note that the BUG_ON() was
provably false for all non-negative inputs (since x % y <= x), so we
dropped it.]

Signed-off-by: default avatarBrian Norris <computersforpeace@gmail.com>
parent bd8898db
Loading
Loading
Loading
Loading
+28 −14
Original line number Diff line number Diff line
@@ -99,22 +99,28 @@ static map_word gf_read(struct map_info *map, unsigned long ofs)
 *	@from: flash offset to copy from
 *	@len:  how much to copy
 *
 * We rely on the MTD layer to chunk up copies such that a single request here
 * will not cross a window size.  This allows us to only wiggle the GPIOs once
 * before falling back to a normal memcpy.  Reading the higher layer code shows
 * that this is indeed the case, but add a BUG_ON() to future proof.
 * The "from" region may straddle more than one window, so toggle the GPIOs for
 * each window region before reading its data.
 */
static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
	struct async_state *state = gf_map_info_to_state(map);

	gf_set_gpios(state, from);
	int this_len;

	/* BUG if operation crosses the win_size */
	BUG_ON(!((from + len) % state->win_size <= (from + len)));
	while (len) {
		if ((from % state->win_size) + len > state->win_size)
			this_len = state->win_size - (from % state->win_size);
		else
			this_len = len;

	/* operation does not cross the win_size, so one shot it */
	memcpy_fromio(to, map->virt + (from % state->win_size), len);
		gf_set_gpios(state, from);
		memcpy_fromio(to, map->virt + (from % state->win_size),
			 this_len);
		len -= this_len;
		from += this_len;
		to += this_len;
	}
}

/**
@@ -147,13 +153,21 @@ static void gf_copy_to(struct map_info *map, unsigned long to,
{
	struct async_state *state = gf_map_info_to_state(map);

	gf_set_gpios(state, to);
	int this_len;

	/* BUG if operation crosses the win_size */
	BUG_ON(!((to + len) % state->win_size <= (to + len)));
	while (len) {
		if ((to % state->win_size) + len > state->win_size)
			this_len = state->win_size - (to % state->win_size);
		else
			this_len = len;

	/* operation does not cross the win_size, so one shot it */
		gf_set_gpios(state, to);
		memcpy_toio(map->virt + (to % state->win_size), from, len);

		len -= this_len;
		to += this_len;
		from += this_len;
	}
}

static const char * const part_probe_types[] = {