xref: /nrf52832-nimble/rt-thread/components/dfs/filesystems/uffs/src/uffs/uffs_utils.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2   This file is part of UFFS, the Ultra-low-cost Flash File System.
3 
4   Copyright (C) 2005-2009 Ricky Zheng <[email protected]>
5 
6   UFFS is free software; you can redistribute it and/or modify it under
7   the GNU Library General Public License as published by the Free Software
8   Foundation; either version 2 of the License, or (at your option) any
9   later version.
10 
11   UFFS is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   or GNU Library General Public License, as applicable, for more details.
15 
16   You should have received a copy of the GNU General Public License
17   and GNU Library General Public License along with UFFS; if not, write
18   to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19   Boston, MA  02110-1301, USA.
20 
21   As a special exception, if other files instantiate templates or use
22   macros or inline functions from this file, or you compile this file
23   and link it with other works to produce a work based on this file,
24   this file does not by itself cause the resulting work to be covered
25   by the GNU General Public License. However the source code for this
26   file must still be made available in accordance with section (3) of
27   the GNU General Public License v2.
28 
29   This exception does not invalidate any other reasons why a work based
30   on this file might be covered by the GNU General Public License.
31 */
32 
33 /**
34  * \file uffs_utils.c
35  * \brief utilities of uffs
36  * \author Ricky Zheng, created 12th May, 2005
37  */
38 #include "uffs_config.h"
39 #include "uffs/uffs_device.h"
40 #include "uffs/uffs_utils.h"
41 #include "uffs/uffs_os.h"
42 #include "uffs/uffs_public.h"
43 #include "uffs/uffs_version.h"
44 #include "uffs/uffs_badblock.h"
45 #include "uffs/uffs_fd.h"
46 #include "uffs/uffs_fs.h"
47 
48 #include <stdio.h>
49 #include <string.h>
50 
51 #define PFX "util: "
52 
53 #define SPOOL(dev) &((dev)->mem.spare_pool)
54 
55 #ifdef CONFIG_USE_GLOBAL_FS_LOCK
56 static OSSEM _global_lock = OSSEM_NOT_INITED;
57 
58 /* global file system lock */
uffs_InitGlobalFsLock(void)59 void uffs_InitGlobalFsLock(void)
60 {
61 	uffs_SemCreate(&_global_lock);
62 }
63 
uffs_ReleaseGlobalFsLock(void)64 void uffs_ReleaseGlobalFsLock(void)
65 {
66 	uffs_SemDelete(&_global_lock);
67 }
68 
uffs_GlobalFsLockLock(void)69 void uffs_GlobalFsLockLock(void)
70 {
71 	uffs_SemWait(_global_lock);
72 }
73 
uffs_GlobalFsLockUnlock(void)74 void uffs_GlobalFsLockUnlock(void)
75 {
76 	uffs_SemSignal(_global_lock);
77 }
78 
79 #else
80 
uffs_InitGlobalFsLock(void)81 void uffs_InitGlobalFsLock(void) {}
uffs_ReleaseGlobalFsLock(void)82 void uffs_ReleaseGlobalFsLock(void) {}
uffs_GlobalFsLockLock(void)83 void uffs_GlobalFsLockLock(void) {}
uffs_GlobalFsLockUnlock(void)84 void uffs_GlobalFsLockUnlock(void) {}
85 
86 #endif
87 
88 
89 #ifdef CONFIG_ENABLE_BAD_BLOCK_VERIFY
_ForceFormatAndCheckBlock(uffs_Device * dev,int block)90 static void _ForceFormatAndCheckBlock(uffs_Device *dev, int block)
91 {
92 	int i, j;
93 	uffs_Buf *buf = NULL;
94 	UBOOL bad = U_TRUE;
95 	URET ret;
96 	struct uffs_FlashOpsSt *ops = dev->ops;
97 	struct uffs_TagStoreSt ts;
98 	u8 *spare = NULL;
99 
100 	buf = uffs_BufClone(dev, NULL);
101 	if (buf == NULL) {
102 		uffs_Perror(UFFS_MSG_SERIOUS,
103 					"Alloc page buffer fail ! Format stoped.");
104 		goto ext;
105 	}
106 
107 	spare = (u8 *)uffs_PoolGet(SPOOL(dev));
108 	if (spare == NULL)
109 		goto ext;
110 
111 	//step 1: Erase, fully fill with 0x0, and check
112 	ret = uffs_FlashEraseBlock(dev, block);
113 	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
114 		goto bad_out;
115 
116 	memset(buf->header, 0, dev->com.pg_size);
117 	memset(&ts, 0, sizeof(ts));
118 	memset(spare, 0, dev->attr->spare_size);
119 
120 	for (i = 0; i < dev->attr->pages_per_block; i++) {
121 		if (ops->WritePageWithLayout)
122 			ret = ops->WritePageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts);
123 		else
124 			ret = ops->WritePage(dev, block, i, buf->header, dev->com.pg_size, spare, dev->attr->spare_size);
125 
126 		if (UFFS_FLASH_IS_BAD_BLOCK(ret))
127 			goto bad_out;
128 	}
129 	for (i = 0; i < dev->attr->pages_per_block; i++) {
130 		memset(buf->header, 0xFF, dev->com.pg_size);
131 		memset(&ts, 0xFF, sizeof(ts));
132 		memset(spare, 0xFF, dev->attr->spare_size);
133 
134 		if (ops->ReadPageWithLayout) {
135 			ret = ops->ReadPageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts, NULL);
136 			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
137 				goto bad_out;
138 			for (j = 0; j < dev->com.pg_size; j++)
139 				if (buf->header[j] != 0)
140 					goto bad_out;
141 			for (j = 0; j < sizeof(ts); j++)
142 				if (((u8 *)&ts)[j] != 0)
143 					goto bad_out;
144 		}
145 		else {
146 			ret = ops->ReadPage(dev, block, i, buf->header, dev->com.pg_size, NULL, spare, dev->attr->spare_size);
147 			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
148 				goto bad_out;
149 			for (j = 0; j < dev->com.pg_size; j++)
150 				if (buf->header[j] != 0)
151 					goto bad_out;
152 			for (j = 0; j < dev->attr->spare_size; j++)
153 				if (spare[j] != 0)
154 					goto bad_out;
155 		}
156 	}
157 
158 	//step 2: Erase, and check
159 	ret = uffs_FlashEraseBlock(dev, block);
160 	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
161 		goto bad_out;
162 
163 	for (i = 0; i < dev->attr->pages_per_block; i++) {
164 		memset(buf->header, 0, dev->com.pg_size);
165 		memset(&ts, 0, sizeof(ts));
166 		memset(spare, 0, dev->attr->spare_size);
167 
168 		if (ops->ReadPageWithLayout) {
169 			ret = ops->ReadPageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts, NULL);
170 			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
171 				goto bad_out;
172 			for (j = 0; j < dev->com.pg_size; j++)
173 				if (buf->header[j] != 0xFF)
174 					goto bad_out;
175 			for (j = 0; j < sizeof(ts); j++)
176 				if (((u8 *)&ts)[j] != 0xFF)
177 					goto bad_out;
178 		}
179 		else {
180 			ret = ops->ReadPage(dev, block, i, buf->header, dev->com.pg_size, NULL, spare, dev->attr->spare_size);
181 			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
182 				goto bad_out;
183 			for (j = 0; j < dev->com.pg_size; j++)
184 				if (buf->header[j] != 0xFF)
185 					goto bad_out;
186 			for (j = 0; j < dev->attr->spare_size; j++)
187 				if (spare[j] != 0xFF)
188 					goto bad_out;
189 		}
190 	}
191 
192 	// format succ
193 	bad = U_FALSE;
194 
195 bad_out:
196 	if (bad == U_TRUE)
197 		uffs_FlashMarkBadBlock(dev, block);
198 ext:
199 	if (buf)
200 		uffs_BufFreeClone(dev, buf);
201 
202 	if (spare)
203 		uffs_PoolPut(SPOOL(dev), spare);
204 
205 	return;
206 }
207 #endif
208 
209 
210 
uffs_FormatDevice(uffs_Device * dev,UBOOL force)211 URET uffs_FormatDevice(uffs_Device *dev, UBOOL force)
212 {
213 	u16 i, slot;
214 	URET ret = U_SUCC;
215 
216 	if (dev == NULL)
217 		return U_FAIL;
218 
219 	if (dev->ops == NULL)
220 		return U_FAIL;
221 
222 	uffs_GlobalFsLockLock();
223 
224 	ret = uffs_BufFlushAll(dev);
225 
226 	if (dev->ref_count > 1 && !force) {
227 		uffs_Perror(UFFS_MSG_NORMAL,
228 					"can't format when dev->ref_count = %d",
229 					dev->ref_count);
230 		ret = U_FAIL;
231 	}
232 
233 	if (ret == U_SUCC && force) {
234 		uffs_DirEntryBufPutAll(dev);
235 		uffs_PutAllObjectBuf(dev);
236 		uffs_FdSignatureIncrease();
237 	}
238 
239 	if (ret == U_SUCC &&
240 		uffs_BufIsAllFree(dev) == U_FALSE &&
241 		!force)
242 	{
243 		uffs_Perror(UFFS_MSG_NORMAL, "some page still in used!");
244 		ret = U_FAIL;
245 	}
246 
247 	if (!force) {
248 		for (slot = 0; ret == U_SUCC && slot < dev->cfg.dirty_groups; slot++) {
249 			if (dev->buf.dirtyGroup[slot].count > 0) {
250 				uffs_Perror(UFFS_MSG_SERIOUS, "there still have dirty pages!");
251 				ret = U_FAIL;
252 			}
253 		}
254 	}
255 
256 	if (ret == U_SUCC)
257 		uffs_BufSetAllEmpty(dev);
258 
259 
260 	if (ret == U_SUCC && uffs_BlockInfoIsAllFree(dev) == U_FALSE && !force) {
261 		uffs_Perror(UFFS_MSG_NORMAL,
262 					"there still have block info cache ? fail to format");
263 		ret = U_FAIL;
264 	}
265 
266 	if (ret == U_SUCC)
267 		uffs_BlockInfoExpireAll(dev);
268 
269 	for (i = dev->par.start; ret == U_SUCC && i <= dev->par.end; i++) {
270 		if (uffs_FlashIsBadBlock(dev, i) == U_FALSE) {
271 			uffs_FlashEraseBlock(dev, i);
272 			if (HAVE_BADBLOCK(dev))
273 				uffs_BadBlockProcess(dev, NULL);
274 		}
275 		else {
276 #ifdef CONFIG_ENABLE_BAD_BLOCK_VERIFY
277 			_ForceFormatAndCheckBlock(dev, i);
278 #endif
279 		}
280 	}
281 
282 	if (ret == U_SUCC && uffs_TreeRelease(dev) == U_FAIL) {
283 		ret = U_FAIL;
284 	}
285 
286 	if (ret == U_SUCC && uffs_TreeInit(dev) == U_FAIL) {
287 		ret = U_FAIL;
288 	}
289 
290 	if (ret == U_SUCC && uffs_BuildTree(dev) == U_FAIL) {
291 		ret = U_FAIL;
292 	}
293 
294 	uffs_GlobalFsLockUnlock();
295 
296 	return ret;
297 }
298 
GetTagName(struct uffs_TagStoreSt * s)299 static const char * GetTagName(struct uffs_TagStoreSt *s)
300 {
301 	const char *name = "UNKNOWN";
302 	struct uffs_NodeTypeNameMapSt maps[] = UFFS_TYPE_NAME_MAP;
303 	int i;
304 
305 	for (i = 0; i < ARRAY_SIZE(maps); i++) {
306 		if (s->type == maps[i].type)
307 			name = maps[i].name;
308 	}
309 
310 	return name;
311 }
312 
DumpBufHex(struct uffs_DeviceSt * dev,const u8 * buf,int len,dump_msg_cb * dump)313 static void DumpBufHex(struct uffs_DeviceSt *dev, const u8* buf, int len, dump_msg_cb *dump)
314 {
315 	int i;
316 	for (i = 0; i < len; i++)
317 		dump(dev, "%02X ", buf[i]);
318 }
319 
320 // return -1 if do not need to read next tag
DumpTag(struct uffs_DeviceSt * dev,int block,int page,uffs_Tags * tag,dump_msg_cb * dump)321 static int DumpTag(struct uffs_DeviceSt *dev, int block, int page, uffs_Tags *tag, dump_msg_cb *dump)
322 {
323 	struct uffs_TagStoreSt *s = &tag->s;
324 	struct uffs_MiniHeaderSt header;
325 	URET ret;
326 
327 	if (!TAG_IS_DIRTY(tag)) {
328 		// is a clean page ?
329 		ret = uffs_LoadMiniHeader(dev, block, page, &header);
330 		if (ret == U_FAIL) {
331 			dump(dev, "Fail to load mini header from page 0\n");
332 		}
333 		else {
334 			if (header.status == 0xFF)
335 				dump(dev, "page %d CLEAN\n", page);
336 			else {
337 				dump(dev, "page %d NOT clean ! header: ", page);
338 				DumpBufHex(dev, (u8 *)&header, sizeof(header), dump);
339 				dump(dev, ", tag: ");
340 				DumpBufHex(dev, (u8 *)s, sizeof(struct uffs_TagStoreSt), dump);
341 				dump(dev, "\n");
342 			}
343 		}
344 		return -1;
345 	}
346 
347 	dump(dev, " - page %2d/%2d %s %d/%d len%4d\n", page, s->page_id, GetTagName(s), s->serial, s->parent, s->data_len);
348 
349 	return 0;
350 }
351 
DumpBlock(struct uffs_DeviceSt * dev,int block,dump_msg_cb * dump)352 static void DumpBlock(struct uffs_DeviceSt *dev, int block, dump_msg_cb *dump)
353 {
354 	int i;
355 	struct uffs_StorageAttrSt *attr = dev->attr;
356 	uffs_Tags tag;
357 	URET ret;
358 
359 	dump(dev, "--- Block %d ---\n", block);
360 
361 	if (uffs_FlashIsBadBlock(dev, block)) {
362 		dump(dev, "Bad block\n\n");
363 		return;
364 	}
365 
366 	for (i = 0; i < attr->pages_per_block; i++) {
367 
368 		memset(&tag, 0xFF, sizeof(tag));
369 		ret = uffs_FlashReadPageTag(dev, block, i, &tag);
370 
371 		if (ret == UFFS_FLASH_IO_ERR) {
372 			dump(dev, "page %d tag I/O error\n", i);
373 			continue;
374 		}
375 		else if (ret == UFFS_FLASH_ECC_FAIL) {
376 			dump(dev, "page %d tag ECC error\n", i);
377 			continue;
378 		}
379 		else if (ret == UFFS_FLASH_NO_ERR || ret == UFFS_FLASH_ECC_OK) {
380 			if (ret == UFFS_FLASH_ECC_OK)
381 				dump(dev, "page %d tag has bit flip, corrected by ECC\n", i);
382 
383 			if (DumpTag(dev, block, i, &tag, dump) == 0)
384 				continue;
385 			else
386 				break;
387 		}
388 		else {
389 			dump(dev, "read page %d tag return unexpected: %d\n", i, ret);
390 			continue;
391 		}
392 	}
393 	dump(dev, "\n");
394 }
395 
uffs_DumpDevice(struct uffs_DeviceSt * dev,dump_msg_cb * dump)396 void uffs_DumpDevice(struct uffs_DeviceSt *dev, dump_msg_cb *dump)
397 {
398 	int i;
399 	for (i = dev->par.start; i <= dev->par.end; i++) {
400 		DumpBlock(dev, i, dump);
401 	}
402 }
403