xref: /aosp_15_r20/external/flashrom/erasure_layout.c (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
1*0d6140beSAndroid Build Coastguard Worker /*
2*0d6140beSAndroid Build Coastguard Worker  * This file is part of the flashrom project.
3*0d6140beSAndroid Build Coastguard Worker  *
4*0d6140beSAndroid Build Coastguard Worker  * Copyright (C) 2022 Aarya Chaumal
5*0d6140beSAndroid Build Coastguard Worker  *
6*0d6140beSAndroid Build Coastguard Worker  * This program is free software; you can redistribute it and/or modify
7*0d6140beSAndroid Build Coastguard Worker  * it under the terms of the GNU General Public License as published by
8*0d6140beSAndroid Build Coastguard Worker  * the Free Software Foundation; either version 2 of the License, or
9*0d6140beSAndroid Build Coastguard Worker  * (at your option) any later version.
10*0d6140beSAndroid Build Coastguard Worker  *
11*0d6140beSAndroid Build Coastguard Worker  * This program is distributed in the hope that it will be useful,
12*0d6140beSAndroid Build Coastguard Worker  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*0d6140beSAndroid Build Coastguard Worker  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*0d6140beSAndroid Build Coastguard Worker  * GNU General Public License for more details.
15*0d6140beSAndroid Build Coastguard Worker  */
16*0d6140beSAndroid Build Coastguard Worker 
17*0d6140beSAndroid Build Coastguard Worker #include <limits.h>
18*0d6140beSAndroid Build Coastguard Worker #include <stdbool.h>
19*0d6140beSAndroid Build Coastguard Worker #include <stdlib.h>
20*0d6140beSAndroid Build Coastguard Worker #include <limits.h>
21*0d6140beSAndroid Build Coastguard Worker #include <string.h>
22*0d6140beSAndroid Build Coastguard Worker 
23*0d6140beSAndroid Build Coastguard Worker #include "flash.h"
24*0d6140beSAndroid Build Coastguard Worker #include "layout.h"
25*0d6140beSAndroid Build Coastguard Worker #include "erasure_layout.h"
26*0d6140beSAndroid Build Coastguard Worker 
calculate_block_count(const struct flashchip * chip,size_t eraser_idx)27*0d6140beSAndroid Build Coastguard Worker static size_t calculate_block_count(const struct flashchip *chip, size_t eraser_idx)
28*0d6140beSAndroid Build Coastguard Worker {
29*0d6140beSAndroid Build Coastguard Worker 	size_t block_count = 0;
30*0d6140beSAndroid Build Coastguard Worker 
31*0d6140beSAndroid Build Coastguard Worker 	chipoff_t addr = 0;
32*0d6140beSAndroid Build Coastguard Worker 	for (size_t i = 0; addr < chip->total_size * 1024; i++) {
33*0d6140beSAndroid Build Coastguard Worker 		const struct eraseblock *block = &chip->block_erasers[eraser_idx].eraseblocks[i];
34*0d6140beSAndroid Build Coastguard Worker 		block_count += block->count;
35*0d6140beSAndroid Build Coastguard Worker 		addr += block->size * block->count;
36*0d6140beSAndroid Build Coastguard Worker 	}
37*0d6140beSAndroid Build Coastguard Worker 
38*0d6140beSAndroid Build Coastguard Worker 	return block_count;
39*0d6140beSAndroid Build Coastguard Worker }
40*0d6140beSAndroid Build Coastguard Worker 
init_eraseblock(struct erase_layout * layout,size_t idx,size_t block_num,chipoff_t start_addr,chipoff_t end_addr,size_t * sub_block_index)41*0d6140beSAndroid Build Coastguard Worker static void init_eraseblock(struct erase_layout *layout, size_t idx, size_t block_num,
42*0d6140beSAndroid Build Coastguard Worker 		chipoff_t start_addr, chipoff_t end_addr, size_t *sub_block_index)
43*0d6140beSAndroid Build Coastguard Worker {
44*0d6140beSAndroid Build Coastguard Worker 	struct eraseblock_data *edata = &layout[idx].layout_list[block_num];
45*0d6140beSAndroid Build Coastguard Worker 	edata->start_addr = start_addr;
46*0d6140beSAndroid Build Coastguard Worker 	edata->end_addr = end_addr;
47*0d6140beSAndroid Build Coastguard Worker 	edata->selected = false;
48*0d6140beSAndroid Build Coastguard Worker 	edata->block_num = block_num;
49*0d6140beSAndroid Build Coastguard Worker 
50*0d6140beSAndroid Build Coastguard Worker 	if (!idx)
51*0d6140beSAndroid Build Coastguard Worker 		return;
52*0d6140beSAndroid Build Coastguard Worker 
53*0d6140beSAndroid Build Coastguard Worker 	edata->first_sub_block_index = *sub_block_index;
54*0d6140beSAndroid Build Coastguard Worker 	struct eraseblock_data *subedata = &layout[idx - 1].layout_list[*sub_block_index];
55*0d6140beSAndroid Build Coastguard Worker 	while (*sub_block_index < layout[idx-1].block_count &&
56*0d6140beSAndroid Build Coastguard Worker 		subedata->start_addr >= start_addr && subedata->end_addr <= end_addr) {
57*0d6140beSAndroid Build Coastguard Worker 		(*sub_block_index)++;
58*0d6140beSAndroid Build Coastguard Worker 		subedata++;
59*0d6140beSAndroid Build Coastguard Worker 	}
60*0d6140beSAndroid Build Coastguard Worker 	edata->last_sub_block_index = *sub_block_index - 1;
61*0d6140beSAndroid Build Coastguard Worker }
62*0d6140beSAndroid Build Coastguard Worker 
63*0d6140beSAndroid Build Coastguard Worker /*
64*0d6140beSAndroid Build Coastguard Worker  * @brief Function to free the created erase_layout
65*0d6140beSAndroid Build Coastguard Worker  *
66*0d6140beSAndroid Build Coastguard Worker  * @param layout pointer to allocated layout
67*0d6140beSAndroid Build Coastguard Worker  * @param erasefn_count number of erase functions for which the layout was created
68*0d6140beSAndroid Build Coastguard Worker  *
69*0d6140beSAndroid Build Coastguard Worker  */
free_erase_layout(struct erase_layout * layout,unsigned int erasefn_count)70*0d6140beSAndroid Build Coastguard Worker void free_erase_layout(struct erase_layout *layout, unsigned int erasefn_count)
71*0d6140beSAndroid Build Coastguard Worker {
72*0d6140beSAndroid Build Coastguard Worker 	if (!layout)
73*0d6140beSAndroid Build Coastguard Worker 		return;
74*0d6140beSAndroid Build Coastguard Worker 	for (size_t i = 0; i < erasefn_count; i++) {
75*0d6140beSAndroid Build Coastguard Worker 		free(layout[i].layout_list);
76*0d6140beSAndroid Build Coastguard Worker 	}
77*0d6140beSAndroid Build Coastguard Worker 	free(layout);
78*0d6140beSAndroid Build Coastguard Worker }
79*0d6140beSAndroid Build Coastguard Worker 
80*0d6140beSAndroid Build Coastguard Worker /*
81*0d6140beSAndroid Build Coastguard Worker  * @brief Function to create an erase layout
82*0d6140beSAndroid Build Coastguard Worker  *
83*0d6140beSAndroid Build Coastguard Worker  * @param	flashctx	flash context
84*0d6140beSAndroid Build Coastguard Worker  * @param	e_layout	address to the pointer to store the layout
85*0d6140beSAndroid Build Coastguard Worker  * @return	0 on success,
86*0d6140beSAndroid Build Coastguard Worker  *		-1 if layout creation fails
87*0d6140beSAndroid Build Coastguard Worker  *
88*0d6140beSAndroid Build Coastguard Worker  * This function creates a layout of which erase functions erase which regions
89*0d6140beSAndroid Build Coastguard Worker  * of the flash chip. This helps to optimally select the erase functions for
90*0d6140beSAndroid Build Coastguard Worker  * erase/write operations.
91*0d6140beSAndroid Build Coastguard Worker  */
create_erase_layout(struct flashctx * const flashctx,struct erase_layout ** e_layout)92*0d6140beSAndroid Build Coastguard Worker int create_erase_layout(struct flashctx *const flashctx, struct erase_layout **e_layout)
93*0d6140beSAndroid Build Coastguard Worker {
94*0d6140beSAndroid Build Coastguard Worker 	const struct flashchip *chip = flashctx->chip;
95*0d6140beSAndroid Build Coastguard Worker 	const size_t erasefn_count = count_usable_erasers(flashctx);
96*0d6140beSAndroid Build Coastguard Worker 	if (!erasefn_count) {
97*0d6140beSAndroid Build Coastguard Worker 		msg_gerr("No erase functions supported\n");
98*0d6140beSAndroid Build Coastguard Worker 		return 0;
99*0d6140beSAndroid Build Coastguard Worker 	}
100*0d6140beSAndroid Build Coastguard Worker 
101*0d6140beSAndroid Build Coastguard Worker 	struct erase_layout *layout = calloc(erasefn_count, sizeof(struct erase_layout));
102*0d6140beSAndroid Build Coastguard Worker 	if (!layout) {
103*0d6140beSAndroid Build Coastguard Worker 		msg_gerr("Out of memory!\n");
104*0d6140beSAndroid Build Coastguard Worker 		return -1;
105*0d6140beSAndroid Build Coastguard Worker 	}
106*0d6140beSAndroid Build Coastguard Worker 
107*0d6140beSAndroid Build Coastguard Worker 	size_t layout_idx = 0;
108*0d6140beSAndroid Build Coastguard Worker 	for (size_t eraser_idx = 0; eraser_idx < NUM_ERASEFUNCTIONS; eraser_idx++) {
109*0d6140beSAndroid Build Coastguard Worker 		if (check_block_eraser(flashctx, eraser_idx, 0))
110*0d6140beSAndroid Build Coastguard Worker 			continue;
111*0d6140beSAndroid Build Coastguard Worker 
112*0d6140beSAndroid Build Coastguard Worker 		layout[layout_idx].eraser = &chip->block_erasers[eraser_idx];
113*0d6140beSAndroid Build Coastguard Worker 		const size_t block_count = calculate_block_count(flashctx->chip, eraser_idx);
114*0d6140beSAndroid Build Coastguard Worker 		size_t sub_block_index = 0;
115*0d6140beSAndroid Build Coastguard Worker 
116*0d6140beSAndroid Build Coastguard Worker 		layout[layout_idx].block_count = block_count;
117*0d6140beSAndroid Build Coastguard Worker 		layout[layout_idx].layout_list = (struct eraseblock_data *)calloc(block_count,
118*0d6140beSAndroid Build Coastguard Worker 									sizeof(struct eraseblock_data));
119*0d6140beSAndroid Build Coastguard Worker 
120*0d6140beSAndroid Build Coastguard Worker 		if (!layout[layout_idx].layout_list) {
121*0d6140beSAndroid Build Coastguard Worker 			free_erase_layout(layout, layout_idx);
122*0d6140beSAndroid Build Coastguard Worker 			return -1;
123*0d6140beSAndroid Build Coastguard Worker 		}
124*0d6140beSAndroid Build Coastguard Worker 
125*0d6140beSAndroid Build Coastguard Worker 		size_t block_num = 0;
126*0d6140beSAndroid Build Coastguard Worker 		chipoff_t start_addr = 0;
127*0d6140beSAndroid Build Coastguard Worker 
128*0d6140beSAndroid Build Coastguard Worker 		for (int i = 0; block_num < block_count;  i++) {
129*0d6140beSAndroid Build Coastguard Worker 			const struct eraseblock *block = &chip->block_erasers[eraser_idx].eraseblocks[i];
130*0d6140beSAndroid Build Coastguard Worker 
131*0d6140beSAndroid Build Coastguard Worker 			for (size_t num = 0; num < block->count; num++) {
132*0d6140beSAndroid Build Coastguard Worker 				chipoff_t end_addr = start_addr + block->size - 1;
133*0d6140beSAndroid Build Coastguard Worker 				init_eraseblock(layout, layout_idx, block_num,
134*0d6140beSAndroid Build Coastguard Worker 						start_addr, end_addr, &sub_block_index);
135*0d6140beSAndroid Build Coastguard Worker 				block_num += 1;
136*0d6140beSAndroid Build Coastguard Worker 				start_addr = end_addr + 1;
137*0d6140beSAndroid Build Coastguard Worker 			}
138*0d6140beSAndroid Build Coastguard Worker 		}
139*0d6140beSAndroid Build Coastguard Worker 		layout_idx++;
140*0d6140beSAndroid Build Coastguard Worker 	}
141*0d6140beSAndroid Build Coastguard Worker 
142*0d6140beSAndroid Build Coastguard Worker 	*e_layout = layout;
143*0d6140beSAndroid Build Coastguard Worker 	return layout_idx;
144*0d6140beSAndroid Build Coastguard Worker }
145*0d6140beSAndroid Build Coastguard Worker 
146*0d6140beSAndroid Build Coastguard Worker /*
147*0d6140beSAndroid Build Coastguard Worker  * @brief	Function to align start and address of the region boundaries
148*0d6140beSAndroid Build Coastguard Worker  *
149*0d6140beSAndroid Build Coastguard Worker  * @param	layout	erase layout
150*0d6140beSAndroid Build Coastguard Worker  * @param	flashctx	flash context
151*0d6140beSAndroid Build Coastguard Worker  * @param	region_start	pointer to start address of the region to align
152*0d6140beSAndroid Build Coastguard Worker  * @param	region_end	pointer to end address of the region to align
153*0d6140beSAndroid Build Coastguard Worker  *
154*0d6140beSAndroid Build Coastguard Worker  * This function aligns start and end address of the region (in struct walk_info)
155*0d6140beSAndroid Build Coastguard Worker  * to some erase sector boundaries and modify the region start and end addresses
156*0d6140beSAndroid Build Coastguard Worker  * to match nearest erase sector boundaries. This function will be used in the
157*0d6140beSAndroid Build Coastguard Worker  * new algorithm for erase function selection.
158*0d6140beSAndroid Build Coastguard Worker  */
align_region(const struct erase_layout * layout,struct flashctx * const flashctx,chipoff_t * region_start,chipoff_t * region_end)159*0d6140beSAndroid Build Coastguard Worker static void align_region(const struct erase_layout *layout, struct flashctx *const flashctx,
160*0d6140beSAndroid Build Coastguard Worker 			chipoff_t *region_start, chipoff_t *region_end)
161*0d6140beSAndroid Build Coastguard Worker {
162*0d6140beSAndroid Build Coastguard Worker 	chipoff_t start_diff = UINT_MAX, end_diff = UINT_MAX;
163*0d6140beSAndroid Build Coastguard Worker 	const size_t erasefn_count = count_usable_erasers(flashctx);
164*0d6140beSAndroid Build Coastguard Worker 	for (size_t i = 0; i < erasefn_count; i++) {
165*0d6140beSAndroid Build Coastguard Worker 		for (size_t j = 0; j < layout[i].block_count; j++) {
166*0d6140beSAndroid Build Coastguard Worker 			const struct eraseblock_data *ll = &layout[i].layout_list[j];
167*0d6140beSAndroid Build Coastguard Worker 			if (ll->start_addr <= *region_start)
168*0d6140beSAndroid Build Coastguard Worker 				start_diff = (*region_start - ll->start_addr) > start_diff ?
169*0d6140beSAndroid Build Coastguard Worker 						start_diff : (*region_start - ll->start_addr);
170*0d6140beSAndroid Build Coastguard Worker 			if (ll->end_addr >= *region_end)
171*0d6140beSAndroid Build Coastguard Worker 				end_diff = (ll->end_addr - *region_end) > end_diff ?
172*0d6140beSAndroid Build Coastguard Worker 						end_diff : (ll->end_addr - *region_end);
173*0d6140beSAndroid Build Coastguard Worker 		}
174*0d6140beSAndroid Build Coastguard Worker 	}
175*0d6140beSAndroid Build Coastguard Worker 
176*0d6140beSAndroid Build Coastguard Worker 	if (start_diff) {
177*0d6140beSAndroid Build Coastguard Worker 		msg_cinfo("Region [0x%08x - 0x%08x] is not sector aligned! "
178*0d6140beSAndroid Build Coastguard Worker 			  "Extending start boundaries by 0x%08x bytes, from 0x%08x -> 0x%08x\n",
179*0d6140beSAndroid Build Coastguard Worker 				*region_start, *region_end,
180*0d6140beSAndroid Build Coastguard Worker 				start_diff, *region_start, *region_start - start_diff);
181*0d6140beSAndroid Build Coastguard Worker 		*region_start = *region_start - start_diff;
182*0d6140beSAndroid Build Coastguard Worker 	}
183*0d6140beSAndroid Build Coastguard Worker 	if (end_diff) {
184*0d6140beSAndroid Build Coastguard Worker 		msg_cinfo("Region [0x%08x - 0x%08x] is not sector aligned! "
185*0d6140beSAndroid Build Coastguard Worker 			  "Extending end boundaries by 0x%08x bytes, from 0x%08x -> 0x%08x\n",
186*0d6140beSAndroid Build Coastguard Worker 				*region_start, *region_end,
187*0d6140beSAndroid Build Coastguard Worker 				end_diff, *region_end, *region_end + end_diff);
188*0d6140beSAndroid Build Coastguard Worker 		*region_end = *region_end + end_diff;
189*0d6140beSAndroid Build Coastguard Worker 	}
190*0d6140beSAndroid Build Coastguard Worker }
191*0d6140beSAndroid Build Coastguard Worker 
192*0d6140beSAndroid Build Coastguard Worker /* Deselect all the blocks from index_to_deselect and down to the smallest. */
deselect_erase_functions(const struct erase_layout * layout,size_t index_to_deselect,int sub_block_start,const int sub_block_end)193*0d6140beSAndroid Build Coastguard Worker static void deselect_erase_functions(const struct erase_layout *layout, size_t index_to_deselect,
194*0d6140beSAndroid Build Coastguard Worker 					int sub_block_start, const int sub_block_end)
195*0d6140beSAndroid Build Coastguard Worker {
196*0d6140beSAndroid Build Coastguard Worker 	for (int j = sub_block_start; j <= sub_block_end; j++)
197*0d6140beSAndroid Build Coastguard Worker 		layout[index_to_deselect].layout_list[j].selected = false;
198*0d6140beSAndroid Build Coastguard Worker 
199*0d6140beSAndroid Build Coastguard Worker 	int block_start_to_deselect =
200*0d6140beSAndroid Build Coastguard Worker 		layout[index_to_deselect].layout_list[sub_block_start].first_sub_block_index;
201*0d6140beSAndroid Build Coastguard Worker 	int block_end_to_deselect =
202*0d6140beSAndroid Build Coastguard Worker 		layout[index_to_deselect].layout_list[sub_block_end].last_sub_block_index;
203*0d6140beSAndroid Build Coastguard Worker 
204*0d6140beSAndroid Build Coastguard Worker 	if (index_to_deselect)
205*0d6140beSAndroid Build Coastguard Worker 		deselect_erase_functions(layout,
206*0d6140beSAndroid Build Coastguard Worker 					index_to_deselect - 1,
207*0d6140beSAndroid Build Coastguard Worker 					block_start_to_deselect,
208*0d6140beSAndroid Build Coastguard Worker 					block_end_to_deselect);
209*0d6140beSAndroid Build Coastguard Worker 	else
210*0d6140beSAndroid Build Coastguard Worker 		return; // index_to_deselect has already reached 0, the smallest size of block. we are done.
211*0d6140beSAndroid Build Coastguard Worker }
212*0d6140beSAndroid Build Coastguard Worker 
213*0d6140beSAndroid Build Coastguard Worker /*
214*0d6140beSAndroid Build Coastguard Worker  * @brief	Function to select the list of sectors that need erasing
215*0d6140beSAndroid Build Coastguard Worker  *
216*0d6140beSAndroid Build Coastguard Worker  * @param	flashctx	flash context
217*0d6140beSAndroid Build Coastguard Worker  * @param	layout		erase layout
218*0d6140beSAndroid Build Coastguard Worker  * @param	findex		index of the erase function
219*0d6140beSAndroid Build Coastguard Worker  * @param	block_num	index of the block to erase according to the erase function index
220*0d6140beSAndroid Build Coastguard Worker  * @param	curcontents	buffer containg the current contents of the flash
221*0d6140beSAndroid Build Coastguard Worker  * @param	newcontents	buffer containg the new contents of the flash
222*0d6140beSAndroid Build Coastguard Worker  * @param	rstart		start address of the region
223*0d6140beSAndroid Build Coastguard Worker  * @rend	rend		end address of the region
224*0d6140beSAndroid Build Coastguard Worker  */
select_erase_functions(struct flashctx * flashctx,const struct erase_layout * layout,size_t findex,size_t block_num,uint8_t * curcontents,uint8_t * newcontents,chipoff_t rstart,chipoff_t rend)225*0d6140beSAndroid Build Coastguard Worker static void select_erase_functions(struct flashctx *flashctx, const struct erase_layout *layout,
226*0d6140beSAndroid Build Coastguard Worker 				size_t findex, size_t block_num, uint8_t *curcontents, uint8_t *newcontents,
227*0d6140beSAndroid Build Coastguard Worker 				chipoff_t rstart, chipoff_t rend)
228*0d6140beSAndroid Build Coastguard Worker {
229*0d6140beSAndroid Build Coastguard Worker 	struct eraseblock_data *ll = &layout[findex].layout_list[block_num];
230*0d6140beSAndroid Build Coastguard Worker 	if (!findex) {
231*0d6140beSAndroid Build Coastguard Worker 		if (ll->start_addr >= rstart && ll->end_addr <= rend) {
232*0d6140beSAndroid Build Coastguard Worker 			chipoff_t start_addr = ll->start_addr;
233*0d6140beSAndroid Build Coastguard Worker 			chipoff_t end_addr = ll->end_addr;
234*0d6140beSAndroid Build Coastguard Worker 			const chipsize_t erase_len = end_addr - start_addr + 1;
235*0d6140beSAndroid Build Coastguard Worker 			const uint8_t erased_value = ERASED_VALUE(flashctx);
236*0d6140beSAndroid Build Coastguard Worker 			ll->selected = need_erase(curcontents + start_addr, newcontents + start_addr, erase_len,
237*0d6140beSAndroid Build Coastguard Worker 						flashctx->chip->gran, erased_value);
238*0d6140beSAndroid Build Coastguard Worker 		}
239*0d6140beSAndroid Build Coastguard Worker 	} else {
240*0d6140beSAndroid Build Coastguard Worker 		int count = 0;
241*0d6140beSAndroid Build Coastguard Worker 		const int sub_block_start = ll->first_sub_block_index;
242*0d6140beSAndroid Build Coastguard Worker 		const int sub_block_end = ll->last_sub_block_index;
243*0d6140beSAndroid Build Coastguard Worker 
244*0d6140beSAndroid Build Coastguard Worker 		for (int j = sub_block_start; j <= sub_block_end; j++) {
245*0d6140beSAndroid Build Coastguard Worker 			select_erase_functions(flashctx, layout, findex - 1, j, curcontents, newcontents,
246*0d6140beSAndroid Build Coastguard Worker 						rstart, rend);
247*0d6140beSAndroid Build Coastguard Worker 			if (layout[findex - 1].layout_list[j].selected)
248*0d6140beSAndroid Build Coastguard Worker 				count++;
249*0d6140beSAndroid Build Coastguard Worker 		}
250*0d6140beSAndroid Build Coastguard Worker 
251*0d6140beSAndroid Build Coastguard Worker 		const int total_blocks = sub_block_end - sub_block_start + 1;
252*0d6140beSAndroid Build Coastguard Worker 		if (count == total_blocks) {
253*0d6140beSAndroid Build Coastguard Worker 			/* We are selecting one large block instead, so send opcode once
254*0d6140beSAndroid Build Coastguard Worker 			 * instead of sending many smaller ones.
255*0d6140beSAndroid Build Coastguard Worker 			 */
256*0d6140beSAndroid Build Coastguard Worker 			if (ll->start_addr >= rstart && ll->end_addr <= rend) {
257*0d6140beSAndroid Build Coastguard Worker 				/* Deselect all smaller blocks covering the same region. */
258*0d6140beSAndroid Build Coastguard Worker 				deselect_erase_functions(layout,
259*0d6140beSAndroid Build Coastguard Worker 							findex - 1,
260*0d6140beSAndroid Build Coastguard Worker 							sub_block_start,
261*0d6140beSAndroid Build Coastguard Worker 							sub_block_end);
262*0d6140beSAndroid Build Coastguard Worker 
263*0d6140beSAndroid Build Coastguard Worker 				/* Select large block. */
264*0d6140beSAndroid Build Coastguard Worker 				ll->selected = true;
265*0d6140beSAndroid Build Coastguard Worker 			}
266*0d6140beSAndroid Build Coastguard Worker 		}
267*0d6140beSAndroid Build Coastguard Worker 	}
268*0d6140beSAndroid Build Coastguard Worker }
269*0d6140beSAndroid Build Coastguard Worker 
270*0d6140beSAndroid Build Coastguard Worker /*
271*0d6140beSAndroid Build Coastguard Worker  * @brief	wrapper to use the erase algorithm
272*0d6140beSAndroid Build Coastguard Worker  *
273*0d6140beSAndroid Build Coastguard Worker  * @param	flashctx	flash context
274*0d6140beSAndroid Build Coastguard Worker  * @param	region_start	start address of the region
275*0d6140beSAndroid Build Coastguard Worker  * @param	region_end	end address of the region
276*0d6140beSAndroid Build Coastguard Worker  * @param       curcontents     buffer containg the current contents of the flash
277*0d6140beSAndroid Build Coastguard Worker  * @param       newcontents     buffer containg the new contents of the flash
278*0d6140beSAndroid Build Coastguard Worker  * @param	erase_layout	erase layout
279*0d6140beSAndroid Build Coastguard Worker  * @param	all_skipped	pointer to the flag to chec if any block was erased
280*0d6140beSAndroid Build Coastguard Worker  */
erase_write(struct flashctx * const flashctx,chipoff_t region_start,chipoff_t region_end,uint8_t * curcontents,uint8_t * newcontents,struct erase_layout * erase_layout,bool * all_skipped)281*0d6140beSAndroid Build Coastguard Worker int erase_write(struct flashctx *const flashctx, chipoff_t region_start, chipoff_t region_end,
282*0d6140beSAndroid Build Coastguard Worker 		uint8_t *curcontents, uint8_t *newcontents,
283*0d6140beSAndroid Build Coastguard Worker 		struct erase_layout *erase_layout, bool *all_skipped)
284*0d6140beSAndroid Build Coastguard Worker {
285*0d6140beSAndroid Build Coastguard Worker 	const size_t erasefn_count = count_usable_erasers(flashctx);
286*0d6140beSAndroid Build Coastguard Worker 	int ret = 0;
287*0d6140beSAndroid Build Coastguard Worker 	size_t i;
288*0d6140beSAndroid Build Coastguard Worker 	chipoff_t old_start = region_start, old_end = region_end;
289*0d6140beSAndroid Build Coastguard Worker 	align_region(erase_layout, flashctx, &region_start, &region_end);
290*0d6140beSAndroid Build Coastguard Worker 
291*0d6140beSAndroid Build Coastguard Worker 	uint8_t *old_start_buf = NULL, *old_end_buf = NULL;
292*0d6140beSAndroid Build Coastguard Worker 	const size_t start_buf_len = old_start - region_start;
293*0d6140beSAndroid Build Coastguard Worker 	const size_t end_buf_len = region_end - old_end;
294*0d6140beSAndroid Build Coastguard Worker 
295*0d6140beSAndroid Build Coastguard Worker 	if (start_buf_len) {
296*0d6140beSAndroid Build Coastguard Worker 		old_start_buf = (uint8_t *)malloc(start_buf_len);
297*0d6140beSAndroid Build Coastguard Worker 		if (!old_start_buf) {
298*0d6140beSAndroid Build Coastguard Worker 			msg_cerr("Not enough memory!\n");
299*0d6140beSAndroid Build Coastguard Worker 			ret = -1;
300*0d6140beSAndroid Build Coastguard Worker 			goto _end;
301*0d6140beSAndroid Build Coastguard Worker 		}
302*0d6140beSAndroid Build Coastguard Worker 		read_flash(flashctx, curcontents + region_start, region_start, start_buf_len);
303*0d6140beSAndroid Build Coastguard Worker 		memcpy(old_start_buf, newcontents + region_start, start_buf_len);
304*0d6140beSAndroid Build Coastguard Worker 		memcpy(newcontents + region_start, curcontents + region_start, start_buf_len);
305*0d6140beSAndroid Build Coastguard Worker 	}
306*0d6140beSAndroid Build Coastguard Worker 	if (end_buf_len) {
307*0d6140beSAndroid Build Coastguard Worker 		chipoff_t end_offset = old_end + 1;
308*0d6140beSAndroid Build Coastguard Worker 		old_end_buf = (uint8_t *)malloc(end_buf_len);
309*0d6140beSAndroid Build Coastguard Worker 		if (!old_end_buf) {
310*0d6140beSAndroid Build Coastguard Worker 			msg_cerr("Not enough memory!\n");
311*0d6140beSAndroid Build Coastguard Worker 			ret = -1;
312*0d6140beSAndroid Build Coastguard Worker 			goto _end;
313*0d6140beSAndroid Build Coastguard Worker 		}
314*0d6140beSAndroid Build Coastguard Worker 		read_flash(flashctx, curcontents + end_offset, end_offset, end_buf_len);
315*0d6140beSAndroid Build Coastguard Worker 		memcpy(old_end_buf, newcontents + end_offset, end_buf_len);
316*0d6140beSAndroid Build Coastguard Worker 		memcpy(newcontents + end_offset, curcontents + end_offset, end_buf_len);
317*0d6140beSAndroid Build Coastguard Worker 	}
318*0d6140beSAndroid Build Coastguard Worker 
319*0d6140beSAndroid Build Coastguard Worker 	// select erase functions
320*0d6140beSAndroid Build Coastguard Worker 	for (i = 0; i < erase_layout[erasefn_count - 1].block_count; i++) {
321*0d6140beSAndroid Build Coastguard Worker 		if (erase_layout[erasefn_count - 1].layout_list[i].start_addr <= region_end &&
322*0d6140beSAndroid Build Coastguard Worker 			region_start <= erase_layout[erasefn_count - 1].layout_list[i].end_addr)
323*0d6140beSAndroid Build Coastguard Worker 			select_erase_functions(flashctx, erase_layout,
324*0d6140beSAndroid Build Coastguard Worker 						erasefn_count - 1, i,
325*0d6140beSAndroid Build Coastguard Worker 						curcontents, newcontents,
326*0d6140beSAndroid Build Coastguard Worker 						region_start, region_end);
327*0d6140beSAndroid Build Coastguard Worker 	}
328*0d6140beSAndroid Build Coastguard Worker 
329*0d6140beSAndroid Build Coastguard Worker 	for (i = 0; i < erasefn_count; i++) {
330*0d6140beSAndroid Build Coastguard Worker 		for (size_t j = 0; j < erase_layout[i].block_count; j++) {
331*0d6140beSAndroid Build Coastguard Worker 			if (!erase_layout[i].layout_list[j].selected)	continue;
332*0d6140beSAndroid Build Coastguard Worker 
333*0d6140beSAndroid Build Coastguard Worker 			// erase
334*0d6140beSAndroid Build Coastguard Worker 			chipoff_t start_addr = erase_layout[i].layout_list[j].start_addr;
335*0d6140beSAndroid Build Coastguard Worker 			unsigned int block_len = erase_layout[i].layout_list[j].end_addr - start_addr + 1;
336*0d6140beSAndroid Build Coastguard Worker 			const uint8_t erased_value = ERASED_VALUE(flashctx);
337*0d6140beSAndroid Build Coastguard Worker 			// execute erase
338*0d6140beSAndroid Build Coastguard Worker 			erasefunc_t *erasefn = lookup_erase_func_ptr(erase_layout[i].eraser);
339*0d6140beSAndroid Build Coastguard Worker 
340*0d6140beSAndroid Build Coastguard Worker 			if (!flashctx->flags.skip_unwritable_regions) {
341*0d6140beSAndroid Build Coastguard Worker 				if (check_for_unwritable_regions(flashctx, start_addr, block_len))
342*0d6140beSAndroid Build Coastguard Worker 					goto _end;
343*0d6140beSAndroid Build Coastguard Worker 			}
344*0d6140beSAndroid Build Coastguard Worker 
345*0d6140beSAndroid Build Coastguard Worker 			unsigned int len;
346*0d6140beSAndroid Build Coastguard Worker 			for (unsigned int addr = start_addr; addr < start_addr + block_len; addr += len) {
347*0d6140beSAndroid Build Coastguard Worker 				struct flash_region region;
348*0d6140beSAndroid Build Coastguard Worker 				get_flash_region(flashctx, addr, &region);
349*0d6140beSAndroid Build Coastguard Worker 
350*0d6140beSAndroid Build Coastguard Worker 				len = min(start_addr + block_len, region.end) - addr;
351*0d6140beSAndroid Build Coastguard Worker 
352*0d6140beSAndroid Build Coastguard Worker 				if (region.write_prot) {
353*0d6140beSAndroid Build Coastguard Worker 					msg_gdbg("%s: cannot erase inside %s "
354*0d6140beSAndroid Build Coastguard Worker 						"region (%#08"PRIx32"..%#08"PRIx32"), skipping range (%#08x..%#08x).\n",
355*0d6140beSAndroid Build Coastguard Worker 						 __func__, region.name,
356*0d6140beSAndroid Build Coastguard Worker 						 region.start, region.end - 1,
357*0d6140beSAndroid Build Coastguard Worker 						 addr, addr + len - 1);
358*0d6140beSAndroid Build Coastguard Worker 					free(region.name);
359*0d6140beSAndroid Build Coastguard Worker 					continue;
360*0d6140beSAndroid Build Coastguard Worker 				}
361*0d6140beSAndroid Build Coastguard Worker 
362*0d6140beSAndroid Build Coastguard Worker 				msg_gdbg("%s: %s region (%#08"PRIx32"..%#08"PRIx32") is "
363*0d6140beSAndroid Build Coastguard Worker 					"writable, erasing range (%#08x..%#08x).\n",
364*0d6140beSAndroid Build Coastguard Worker 					 __func__, region.name,
365*0d6140beSAndroid Build Coastguard Worker 					 region.start, region.end - 1,
366*0d6140beSAndroid Build Coastguard Worker 					 addr, addr + len - 1);
367*0d6140beSAndroid Build Coastguard Worker 				free(region.name);
368*0d6140beSAndroid Build Coastguard Worker 
369*0d6140beSAndroid Build Coastguard Worker 				if (erasefn(flashctx, addr, len)) {
370*0d6140beSAndroid Build Coastguard Worker 					ret = -1;
371*0d6140beSAndroid Build Coastguard Worker 					goto _end;
372*0d6140beSAndroid Build Coastguard Worker 				}
373*0d6140beSAndroid Build Coastguard Worker 				if (check_erased_range(flashctx, addr, len)) {
374*0d6140beSAndroid Build Coastguard Worker 					ret = - 1;
375*0d6140beSAndroid Build Coastguard Worker 					msg_cerr("ERASE FAILED!\n");
376*0d6140beSAndroid Build Coastguard Worker 					goto _end;
377*0d6140beSAndroid Build Coastguard Worker 				}
378*0d6140beSAndroid Build Coastguard Worker 			}
379*0d6140beSAndroid Build Coastguard Worker 
380*0d6140beSAndroid Build Coastguard Worker 			// adjust curcontents
381*0d6140beSAndroid Build Coastguard Worker 			memset(curcontents+start_addr, erased_value, block_len);
382*0d6140beSAndroid Build Coastguard Worker 			// after erase make it unselected again
383*0d6140beSAndroid Build Coastguard Worker 			erase_layout[i].layout_list[j].selected = false;
384*0d6140beSAndroid Build Coastguard Worker 			msg_cdbg("E(%"PRIx32":%"PRIx32")", start_addr, start_addr + block_len - 1);
385*0d6140beSAndroid Build Coastguard Worker 
386*0d6140beSAndroid Build Coastguard Worker 			*all_skipped = false;
387*0d6140beSAndroid Build Coastguard Worker 		}
388*0d6140beSAndroid Build Coastguard Worker 	}
389*0d6140beSAndroid Build Coastguard Worker 
390*0d6140beSAndroid Build Coastguard Worker 	// write
391*0d6140beSAndroid Build Coastguard Worker 	unsigned int start_here = 0, len_here = 0, erase_len = region_end - region_start + 1;
392*0d6140beSAndroid Build Coastguard Worker 	while ((len_here = get_next_write(curcontents + region_start + start_here,
393*0d6140beSAndroid Build Coastguard Worker 					newcontents + region_start + start_here,
394*0d6140beSAndroid Build Coastguard Worker 					erase_len - start_here, &start_here,
395*0d6140beSAndroid Build Coastguard Worker 					flashctx->chip->gran))) {
396*0d6140beSAndroid Build Coastguard Worker 		// execute write
397*0d6140beSAndroid Build Coastguard Worker 		ret = write_flash(flashctx,
398*0d6140beSAndroid Build Coastguard Worker 				newcontents + region_start + start_here,
399*0d6140beSAndroid Build Coastguard Worker 				region_start + start_here, len_here);
400*0d6140beSAndroid Build Coastguard Worker 		if (ret) {
401*0d6140beSAndroid Build Coastguard Worker 			msg_cerr("Write failed at %#zx, Abort.\n", i);
402*0d6140beSAndroid Build Coastguard Worker 			ret = -1;
403*0d6140beSAndroid Build Coastguard Worker 			goto _end;
404*0d6140beSAndroid Build Coastguard Worker 		}
405*0d6140beSAndroid Build Coastguard Worker 
406*0d6140beSAndroid Build Coastguard Worker 		// adjust curcontents
407*0d6140beSAndroid Build Coastguard Worker 		memcpy(curcontents + region_start + start_here,
408*0d6140beSAndroid Build Coastguard Worker 			newcontents + region_start + start_here, len_here);
409*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("W(%"PRIx32":%"PRIx32")", region_start + start_here, region_start + start_here + len_here - 1);
410*0d6140beSAndroid Build Coastguard Worker 
411*0d6140beSAndroid Build Coastguard Worker 		*all_skipped = false;
412*0d6140beSAndroid Build Coastguard Worker 	}
413*0d6140beSAndroid Build Coastguard Worker 
414*0d6140beSAndroid Build Coastguard Worker _end:
415*0d6140beSAndroid Build Coastguard Worker 	if (old_start_buf) {
416*0d6140beSAndroid Build Coastguard Worker 		memcpy(newcontents + region_start, old_start_buf, start_buf_len);
417*0d6140beSAndroid Build Coastguard Worker 		free(old_start_buf);
418*0d6140beSAndroid Build Coastguard Worker 	}
419*0d6140beSAndroid Build Coastguard Worker 
420*0d6140beSAndroid Build Coastguard Worker 	if (old_end_buf) {
421*0d6140beSAndroid Build Coastguard Worker 		memcpy(newcontents + old_end, old_end_buf, end_buf_len);
422*0d6140beSAndroid Build Coastguard Worker 		free(old_end_buf);
423*0d6140beSAndroid Build Coastguard Worker 	}
424*0d6140beSAndroid Build Coastguard Worker 
425*0d6140beSAndroid Build Coastguard Worker 	msg_cinfo("Erase/write done from %"PRIx32" to %"PRIx32"\n", region_start, region_end);
426*0d6140beSAndroid Build Coastguard Worker 	return ret;
427*0d6140beSAndroid Build Coastguard Worker }
428