2 * ext2 filesystem for onenand device.
4 * Copyright(C) 2009 Samsung Electronics
5 * InKi Dae <inki.dae@samsung.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
29 #include <linux/types.h>
31 #include <asm/global_data.h>
33 #include <onenand_ext2.h>
35 #define IMAGE_BASE (0x380000)
36 #define IMAGE_SIZE (8 * 1024 * 1024)
37 #define ONENAND_READ_SIZE (4 * 1024)
39 #define BOOT_BLOCK_SIZE (1024)
40 #define DATA_BLOCK_UNIT (512)
41 #define INODE_TABLE_ENTRY_SIZE (128)
43 extern int onenand_read(ulong off, char *buf, unsigned int *out_size);
45 /* it indicates block size of ext2 filesystem formatted. */
46 unsigned int block_size_of_fs = 0;
48 /* it indicates inode block number for root directory and current directory. */
49 unsigned int root_inode, current_inode;
51 /* it indicates inode block size for finding the end of directory entry. */
52 unsigned int inode_block_size = 0;
54 /* it indicates the location of inode table in group structure. */
55 unsigned int inode_table_location;
57 /* it is used to store contents of block data and table entry. */
58 char data_buf[ONENAND_READ_SIZE], table_buf[ONENAND_READ_SIZE];
61 * read a block from onenand.
63 * @offset : onenand offset for reading a block.
64 * @blk_count : block count to be read.
66 * return value is the size read from onenand.
68 static unsigned int read_blk_from_onenand(unsigned int offset, unsigned int blk_count, char *buf)
70 unsigned int size, i, out_size = 0;
72 for (i = 0; i < blk_count; i++) {
73 offset += (i * ONENAND_READ_SIZE);
74 onenand_read(IMAGE_BASE + offset, buf, &size);
82 static int get_sblock(struct ext2_sblock *sblock)
86 pbuf = (char *) table_buf;
88 /* first, read 4K block from onenand. */
89 read_blk_from_onenand(0, 1, pbuf);
91 /* sblock places in next to 1k, boot sector so read from there. */
92 memcpy(sblock, pbuf + BOOT_BLOCK_SIZE, sizeof(struct ext2_sblock));
94 dprint("total_inodes = %d, block_size = %d\n", sblock->total_inodes,
95 sblock->log2_block_size);
100 static int get_group_dec(struct ext2_block_group *group)
103 unsigned int offset = 0;
105 pbuf = (char *) table_buf;
107 /* get group descriptor. */
108 if (block_size_of_fs == 1024)
109 /* boot block : 1k, super block : 1k. */
110 offset = block_size_of_fs * 2;
111 else if (block_size_of_fs == 4096)
112 /* boot block : 1k, super block : 3k. */
113 offset = block_size_of_fs;
115 read_blk_from_onenand(offset, 1, pbuf);
117 memcpy(group, pbuf, sizeof(struct ext2_block_group));
119 dprint("block_id = %d, inode_id = %d, inode_table_id = %d\n",
120 group->block_id, group->inode_id, group->inode_table_id);
125 static int get_root_inode_entry(struct ext2_inode *inode,
126 struct ext2_block_group *group)
130 pbuf = (char *) table_buf;
133 dprint("group is NULL.\n");
137 /* get location of inode table. */
138 inode_table_location = group->inode_table_id * block_size_of_fs;
140 dprint("inode table location = %d\n", inode_table_location);
142 read_blk_from_onenand(inode_table_location, 1, pbuf);
145 * inode table has 128byte per entry and
146 * root inode places in second entry.
148 memcpy(inode, pbuf + INODE_TABLE_ENTRY_SIZE,
149 sizeof(struct ext2_inode));
151 /* get inode block size and used to find the end of directory entry. */
152 inode_block_size = inode->blockcnt * DATA_BLOCK_UNIT;
154 dprint("dir_blocks = 0x%8x, size = %d, blockcnt = %d\n", inode->b.blocks.dir_blocks[0],
155 inode->size, inode->blockcnt);
161 * get root directory entry.
163 * @dirent : directory entry structure for storing directory entry value.
164 * @inode : instance pointer of inode entry.
166 * the offset of root directory entry is returned.
169 static unsigned int get_root_dir_entry(struct ext2_dirent *dirent, struct ext2_inode *inode)
172 unsigned int offset = 0;
174 pbuf = (char *) table_buf;
177 dprint("inode is NULL.\n");
181 /* get block number stored with data of inode. */
182 offset = inode->b.blocks.dir_blocks[0] * block_size_of_fs;
184 read_blk_from_onenand(offset, 1, pbuf);
186 memcpy(dirent, pbuf, sizeof(struct ext2_dirent));
188 dprint("first entry name of root directory is name = %s\n",
194 /* initialize ext2 filesystem and return pointer of root directory entry. */
195 unsigned int mount_ext2fs(void)
197 struct ext2_sblock sblock;
198 struct ext2_block_group group;
199 struct ext2_inode inode;
200 struct ext2_dirent dirent;
202 unsigned int need_block = 0, ret = 0, offset = 0;
204 ret = get_sblock(&sblock);
206 dprint("sblock is NULL.\n");
211 * in case that log2_block_size is 0, block_size is 1024,
212 * 2048 for 1 and 4096 for 2.
214 switch (sblock.log2_block_size + 1) {
216 block_size_of_fs = 1024;
219 block_size_of_fs = 2048;
222 block_size_of_fs = 4096;
225 block_size_of_fs = 4096;
229 dprint("block size of filesystem = %d\n", block_size_of_fs);
231 /* get block size for inode table. */
232 need_block = sblock.total_inodes * INODE_TABLE_ENTRY_SIZE /
235 dprint("need_block for inode table = %d\n", need_block);
237 ret = get_group_dec(&group);
239 dprint("group_dec is NULL.\n");
243 ret = get_root_inode_entry(&inode, &group);
245 dprint("root_inode_entry is NULL.\n");
249 offset = get_root_dir_entry(&dirent, &inode);
251 dprint("root_dir_entry is NULL.\n");
261 * @inode_table : the offset of inode table to file.
262 * @inode : inode structure for storing inode entry value.
264 static int get_inode(unsigned int inode_table, struct ext2_inode *inode)
266 unsigned int offset, extra_block_count, need_block = 0;
269 pbuf = (char *) table_buf;
271 /* get block count. */
272 need_block = (inode_table / block_size_of_fs);
275 * get extra block count for loading from onenand device
276 * considering 1k and 2k block size for filesystem.
278 extra_block_count = (block_size_of_fs * need_block) / ONENAND_READ_SIZE;
280 offset = inode_table_location + (ONENAND_READ_SIZE * extra_block_count);
282 read_blk_from_onenand(offset, 1, pbuf);
284 /* also subtract as increased offset. */
285 inode_table -= (ONENAND_READ_SIZE * extra_block_count);
287 /* get inode entry to file. */
288 memcpy(inode, (char *) pbuf + inode_table, sizeof(struct ext2_inode));
294 * get directory entry to file.
296 * @in_buf : pointer of system memory to directory entry.
297 * @dirent : directory entry structure for storing directory entry value.
299 static int get_dir_entry(char *in_buf, struct ext2_dirent *dirent)
301 memcpy(dirent, (char *) in_buf, sizeof(struct ext2_dirent));
303 dirent->name[dirent->namelen] = '\0';
310 * @dirent : instance pointer to directory entry structure.
312 * return value is the offset to inode table.
314 static int get_inode_offset(struct ext2_dirent *dirent)
318 if (dirent == NULL) {
319 printf("dirent is NULL.\n");
323 inode_table = (dirent->inode - 1) * INODE_TABLE_ENTRY_SIZE;
329 * move in_inode which is directory entry pointer to next directory entry.
331 * @dirent : pointer of ext2_dirent structure.
333 static unsigned int next_dir_entry(struct ext2_dirent *dirent)
335 static unsigned int sum_point = 0;
338 * it finds the end of directory entry.
340 * directroy entries are stored in data block.
341 * (inode_block_size = data block count * data block size.)
343 sum_point += dirent->direntlen;
344 if ( sum_point >= inode_block_size) {
350 return dirent->direntlen;
354 * find file matched with filename.
356 * @in_inode : the offset of current directory entry.
357 * @filename : the file name for finding.
358 * @dirent : the pointer of directory entry structure found.
360 * return value : inode number of directory entry.
362 int find_file_ext2(unsigned int in_inode, const char *filename,
363 struct ext2_dirent *dirent)
365 unsigned int next_point = 0;
368 pbuf = (char *) table_buf;
370 read_blk_from_onenand(in_inode, 1, pbuf);
372 get_dir_entry(pbuf, dirent);
374 if ((strcmp(dirent->name, filename)) == 0) {
375 return dirent->inode;
378 next_point = next_dir_entry(dirent);
381 get_dir_entry(pbuf, dirent);
383 dprint("soure file = %s, dst file = %s, len = %d\n", filename,
384 dirent->name, dirent->namelen);
386 if ((strcmp(dirent->name, filename)) == 0) {
387 return dirent->inode;
390 next_point = next_dir_entry(dirent);
393 } while (next_point > 0);
395 printf("failed to find file.\n");
401 * get the location of inode to file.
403 * @in_inode : the offset to current directory entry.
406 * return value is the offset of inode table to file.
408 int open_file_ext2(unsigned int in_inode, const char *name)
410 unsigned int directory_inode, inode_table;
411 struct ext2_dirent dirent;
413 directory_inode = find_file_ext2(in_inode, name, &dirent);
416 * the location of file block =
417 * inode table base + (directory inode number - 1) * inode table entry size.
419 inode_table = (directory_inode - 1) * INODE_TABLE_ENTRY_SIZE;
421 dprint("directory_inode = %d, inode_table = %d\n", directory_inode, inode_table);
429 * @inode_table : offset of inode table to file.
430 * @dirent : pointer of ext2_dirent structure.
432 int get_filesize_ext2(unsigned int inode_table)
434 struct ext2_inode inode;
436 get_inode(inode_table, &inode);
441 /* read data block to file.
443 * @inode_table : offset of inode table to file.
444 * @in_buf : memory buffer for storing contents of data block.
447 int read_file_ext2(unsigned int inode_table, char *in_buf, unsigned int size)
449 struct ext2_datablock d_block;
450 struct ext2_inode inode;
451 unsigned int d_block_addr, id_block_addr, read_size = 0, id_block_num;
452 char *ptable = NULL, *pdata = NULL;
455 ptable = (char *) table_buf;
456 pdata = (char *) data_buf;
458 get_inode(inode_table, &inode);
460 /* get data block information for file. */
461 memcpy(&d_block, (char *) &inode.b.blocks, sizeof(struct ext2_datablock));
463 /* get real data from direct blocks. */
464 for (i = 0; i < INDIRECT_BLOCKS; i++) {
465 if (d_block.dir_blocks[i] > 0) {
467 dprint("direct block[%d] num = %d\n", i, d_block.dir_blocks[i]);
469 /* calculate real data address. */
470 d_block_addr = (unsigned int) d_block.dir_blocks[i] *
472 if (size > block_size_of_fs) {
473 read_blk_from_onenand(d_block_addr, 1, pdata);
474 memcpy(in_buf, (char *) pdata, block_size_of_fs);
475 size -= block_size_of_fs;
476 in_buf += block_size_of_fs;
477 read_size += block_size_of_fs;
482 read_blk_from_onenand(d_block_addr, 1, pdata);
483 memcpy(in_buf, (char *) pdata, size);
492 * get real data from 1-dim indirect blocks only in case that
493 * 2-dim indirect block has no block num.
495 if (d_block.indir_block > 0 && d_block.double_indir_block <= 0) {
496 d_block_addr = (unsigned int) d_block.indir_block *
498 dprint("1-dim indirect block address = 0x%x\n", d_block_addr);
499 read_blk_from_onenand(d_block_addr, 1, ptable);
501 /* 1-dim indirect block has 1k block and the size per entry is 4byte. */
502 for (i = 0; i < block_size_of_fs; i+=4) {
503 /* get indirect block number. */
504 memcpy(&id_block_num, (char *) ptable + i, 4);
506 dprint("1-dim indirect block[%d] num = %d\n", i, id_block_num);
508 /* get real data from indirect block in case of hole. */
509 if (id_block_num > 0) {
510 /* calculate block number having real data. */
511 id_block_addr = (unsigned int) id_block_num *
514 if (size > block_size_of_fs) {
515 read_blk_from_onenand(id_block_addr, 1, pdata);
516 memcpy(in_buf, (char *) pdata, block_size_of_fs);
517 size -= block_size_of_fs;
518 in_buf += block_size_of_fs;
519 read_size += block_size_of_fs;
524 read_blk_from_onenand(id_block_addr, 1, pdata);
525 memcpy(in_buf, pdata, size);
534 /* read operation for 2-dim and 3-dim indirect blocks should be added. */
540 * list files in directory.
542 * @in_inode : the offset of current directory entry.
543 * @cmd : command indicating file attributes for listing.
545 void ls_ext2(unsigned int in_inode, const int cmd)
547 struct ext2_dirent dirent;
548 struct ext2_inode inode;
549 unsigned int next_point = 0;
550 int ret, inode_table = 0;
553 buf = malloc(ONENAND_READ_SIZE);
555 dprint("failed to allocate memory.\n");
559 read_blk_from_onenand(in_inode, 1, buf);
562 case EXT2_LS_ONLY_FILE:
563 case EXT2_LS_ALL_ENTRY:
566 ret = get_dir_entry(buf, &dirent);
568 dprint("failed to get directory entry.\n");
572 inode_table = get_inode_offset(&dirent);
573 if (inode_table < 0) {
574 dprint("failed to get inode table offset.\n");
578 ret = get_inode(inode_table, &inode);
580 dprint("failed to get inode entry.\n");
584 if (cmd == EXT2_LS_ONLY_FILE) {
585 if (dirent.filetype == EXT2_FT_REG_FILE ||
586 dirent.filetype == EXT2_FT_DIR) {
587 if (dirent.filetype == EXT2_FT_DIR)
588 printf("[ %s ]\n", dirent.name);
590 printf("%s\n", dirent.name);
592 } else if (cmd == EXT2_LS_ALL_ENTRY) {
593 if (dirent.filetype == EXT2_FT_DIR)
594 printf("[ %s ]\n", dirent.name);
596 printf("%s\n", dirent.name);
597 } else if (cmd == EXT2_LS_ALL) {
598 if (dirent.filetype == EXT2_FT_DIR)
599 printf("%x %d [ %s ]\n",
600 inode.mode, inode.size, dirent.name);
603 inode.mode, inode.size, dirent.name);
606 next_point = next_dir_entry(&dirent);
608 } while (next_point != 0);
612 printf("it's wrong command.\n");
620 * change current directory.
622 * @in_inode : the offset of current directory entry.
623 * @name : directory name.
625 void cd_ext2(unsigned int in_inode, const char *name)
627 unsigned int d_inode, out_inode, need_block = 0;
628 struct ext2_inode inode;
629 struct ext2_dirent dirent;
632 /* return inode of directory entry. */
633 out_inode = find_file_ext2(in_inode, name, &dirent);
635 printf("failed to find file.\n");
639 /* check whether entry is director or not. */
640 if (dirent.filetype != EXT2_FT_DIR) {
641 printf("%s is not directory.\n", name);
645 d_inode = (dirent.inode - 1) * INODE_TABLE_ENTRY_SIZE;
648 * get block size for loading from onenand.
649 * if d_inode is more then 4k then need_block should be more then 2.
651 need_block = (d_inode / block_size_of_fs) + 1;
653 buf = malloc(ONENAND_READ_SIZE * need_block);
655 printf("failed to allocate memory.\n");
659 read_blk_from_onenand(inode_table_location, need_block, buf);
661 /* get inode entry to file. */
662 memcpy(&inode, (char *) buf + d_inode, sizeof(struct ext2_inode));
664 current_inode = (inode.b.blocks.dir_blocks[0] * block_size_of_fs);
671 * @in_inode : the offset of current directory entry.
674 void fd_ext2(unsigned int in_inode, const char *name)
676 unsigned int in_size, inode_table, out_size;
679 inode_table = open_file_ext2(in_inode, name);
680 in_size = get_filesize_ext2(inode_table);
682 printf("failed to get file size.\n");
686 dprint("name = %s, in size = %d\n", name, in_size);
688 buf = malloc(in_size);
690 out_size = read_file_ext2(inode_table, buf, in_size);
691 dprint("out size = %d, data address = 0x%8x\n", out_size, (unsigned int) buf);
694 void init_onenand_ext2(void)
696 root_inode = (unsigned int) mount_ext2fs();
697 if (root_inode < 0) {
698 printf("failed to mount.\n");
702 dprint("ext2fs has been mounted.\n");
704 current_inode = root_inode;
707 int do_ls_ext2(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
711 if (argc == 1 && argv[1] == NULL) {
712 cmd = EXT2_LS_ONLY_FILE;
713 } else if (argc == 2 && (strcmp(argv[1], "-a") == 0)) {
714 cmd = EXT2_LS_ALL_ENTRY;
715 } else if (argc == 2 && (strcmp(argv[1], "-al") == 0)) {
719 ls_ext2(current_inode, cmd);
724 int do_cd_ext2(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
726 if (argc == 2 && argv[1] != NULL)
727 cd_ext2(current_inode, argv[1]);
732 int do_fd_ext2(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
734 if (argc == 2 && argv[1] != NULL)
735 fd_ext2(current_inode, argv[1]);
740 U_BOOT_CMD(ls_ext2, 3, 1, do_ls_ext2,
741 "list files from ext2 filesystem.\n",
742 "ls_ext2 [-al] [direct_name]\n"
745 U_BOOT_CMD(cd_ext2, 3, 1, do_cd_ext2,
746 "change directory.\n",
747 "cd_ext2 [direct_name]\n"
750 U_BOOT_CMD(fd_ext2, 3, 1, do_fd_ext2,
752 "fd_ext2 [file_name]\n"