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