1a1596438SUma Shankar /* 2a1596438SUma Shankar * (C) Copyright 2011 - 2012 Samsung Electronics 3a1596438SUma Shankar * EXT4 filesystem implementation in Uboot by 4a1596438SUma Shankar * Uma Shankar <uma.shankar@samsung.com> 5a1596438SUma Shankar * Manjunatha C Achar <a.manjunatha@samsung.com> 6a1596438SUma Shankar * 7a1596438SUma Shankar * ext4ls and ext4load : Based on ext2 ls and load support in Uboot. 8a1596438SUma Shankar * Ext4 read optimization taken from Open-Moko 9a1596438SUma Shankar * Qi bootloader 10a1596438SUma Shankar * 11a1596438SUma Shankar * (C) Copyright 2004 12a1596438SUma Shankar * esd gmbh <www.esd-electronics.com> 13a1596438SUma Shankar * Reinhard Arlt <reinhard.arlt@esd-electronics.com> 14a1596438SUma Shankar * 15a1596438SUma Shankar * based on code from grub2 fs/ext2.c and fs/fshelp.c by 16a1596438SUma Shankar * GRUB -- GRand Unified Bootloader 17a1596438SUma Shankar * Copyright (C) 2003, 2004 Free Software Foundation, Inc. 18a1596438SUma Shankar * 19ed34f34dSUma Shankar * ext4write : Based on generic ext4 protocol. 20ed34f34dSUma Shankar * 21a1596438SUma Shankar * This program is free software; you can redistribute it and/or modify 22a1596438SUma Shankar * it under the terms of the GNU General Public License as published by 23a1596438SUma Shankar * the Free Software Foundation; either version 2 of the License, or 24a1596438SUma Shankar * (at your option) any later version. 25a1596438SUma Shankar * 26a1596438SUma Shankar * This program is distributed in the hope that it will be useful, 27a1596438SUma Shankar * but WITHOUT ANY WARRANTY; without even the implied warranty of 28a1596438SUma Shankar * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29a1596438SUma Shankar * GNU General Public License for more details. 30a1596438SUma Shankar * 31a1596438SUma Shankar * You should have received a copy of the GNU General Public License 32a1596438SUma Shankar * along with this program; if not, write to the Free Software 33a1596438SUma Shankar * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 34a1596438SUma Shankar */ 35a1596438SUma Shankar 36a1596438SUma Shankar #include <common.h> 37a1596438SUma Shankar #include <ext_common.h> 38a1596438SUma Shankar #include <ext4fs.h> 39a1596438SUma Shankar #include "ext4_common.h" 40a1596438SUma Shankar 41a1596438SUma Shankar int ext4fs_symlinknest; 4294501062SRob Herring struct ext_filesystem ext_fs; 43a1596438SUma Shankar 44a1596438SUma Shankar struct ext_filesystem *get_fs(void) 45a1596438SUma Shankar { 4694501062SRob Herring return &ext_fs; 47a1596438SUma Shankar } 48a1596438SUma Shankar 49a1596438SUma Shankar void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot) 50a1596438SUma Shankar { 51a1596438SUma Shankar if ((node != &ext4fs_root->diropen) && (node != currroot)) 52a1596438SUma Shankar free(node); 53a1596438SUma Shankar } 54a1596438SUma Shankar 55a1596438SUma Shankar /* 56a1596438SUma Shankar * Taken from openmoko-kernel mailing list: By Andy green 57a1596438SUma Shankar * Optimized read file API : collects and defers contiguous sector 58a1596438SUma Shankar * reads into one potentially more efficient larger sequential read action 59a1596438SUma Shankar */ 60a1596438SUma Shankar int ext4fs_read_file(struct ext2fs_node *node, int pos, 61a1596438SUma Shankar unsigned int len, char *buf) 62a1596438SUma Shankar { 6350ce4c07SEgbert Eich struct ext_filesystem *fs = get_fs(); 64a1596438SUma Shankar int i; 65*04735e9cSFrederic Leroy lbaint_t blockcnt; 6650ce4c07SEgbert Eich int log2blksz = fs->dev_desc->log2blksz; 6750ce4c07SEgbert Eich int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz; 6850ce4c07SEgbert Eich int blocksize = (1 << (log2_fs_blocksize + log2blksz)); 69a1596438SUma Shankar unsigned int filesize = __le32_to_cpu(node->inode.size); 70*04735e9cSFrederic Leroy lbaint_t previous_block_number = -1; 71*04735e9cSFrederic Leroy lbaint_t delayed_start = 0; 72*04735e9cSFrederic Leroy lbaint_t delayed_extent = 0; 73*04735e9cSFrederic Leroy lbaint_t delayed_skipfirst = 0; 74*04735e9cSFrederic Leroy lbaint_t delayed_next = 0; 75a1596438SUma Shankar char *delayed_buf = NULL; 76a1596438SUma Shankar short status; 77a1596438SUma Shankar 78a1596438SUma Shankar /* Adjust len so it we can't read past the end of the file. */ 79a1596438SUma Shankar if (len > filesize) 80a1596438SUma Shankar len = filesize; 81a1596438SUma Shankar 82a1596438SUma Shankar blockcnt = ((len + pos) + blocksize - 1) / blocksize; 83a1596438SUma Shankar 84a1596438SUma Shankar for (i = pos / blocksize; i < blockcnt; i++) { 85*04735e9cSFrederic Leroy lbaint_t blknr; 86a1596438SUma Shankar int blockoff = pos % blocksize; 87a1596438SUma Shankar int blockend = blocksize; 88a1596438SUma Shankar int skipfirst = 0; 89a1596438SUma Shankar blknr = read_allocated_block(&(node->inode), i); 90a1596438SUma Shankar if (blknr < 0) 91a1596438SUma Shankar return -1; 92a1596438SUma Shankar 9350ce4c07SEgbert Eich blknr = blknr << log2_fs_blocksize; 94a1596438SUma Shankar 95a1596438SUma Shankar /* Last block. */ 96a1596438SUma Shankar if (i == blockcnt - 1) { 97a1596438SUma Shankar blockend = (len + pos) % blocksize; 98a1596438SUma Shankar 99a1596438SUma Shankar /* The last portion is exactly blocksize. */ 100a1596438SUma Shankar if (!blockend) 101a1596438SUma Shankar blockend = blocksize; 102a1596438SUma Shankar } 103a1596438SUma Shankar 104a1596438SUma Shankar /* First block. */ 105a1596438SUma Shankar if (i == pos / blocksize) { 106a1596438SUma Shankar skipfirst = blockoff; 107a1596438SUma Shankar blockend -= skipfirst; 108a1596438SUma Shankar } 109a1596438SUma Shankar if (blknr) { 110a1596438SUma Shankar int status; 111a1596438SUma Shankar 112a1596438SUma Shankar if (previous_block_number != -1) { 113a1596438SUma Shankar if (delayed_next == blknr) { 114a1596438SUma Shankar delayed_extent += blockend; 11550ce4c07SEgbert Eich delayed_next += blockend >> log2blksz; 116a1596438SUma Shankar } else { /* spill */ 117a1596438SUma Shankar status = ext4fs_devread(delayed_start, 118a1596438SUma Shankar delayed_skipfirst, 119a1596438SUma Shankar delayed_extent, 120a1596438SUma Shankar delayed_buf); 121a1596438SUma Shankar if (status == 0) 122a1596438SUma Shankar return -1; 123a1596438SUma Shankar previous_block_number = blknr; 124a1596438SUma Shankar delayed_start = blknr; 125a1596438SUma Shankar delayed_extent = blockend; 126a1596438SUma Shankar delayed_skipfirst = skipfirst; 127a1596438SUma Shankar delayed_buf = buf; 128a1596438SUma Shankar delayed_next = blknr + 12950ce4c07SEgbert Eich (blockend >> log2blksz); 130a1596438SUma Shankar } 131a1596438SUma Shankar } else { 132a1596438SUma Shankar previous_block_number = blknr; 133a1596438SUma Shankar delayed_start = blknr; 134a1596438SUma Shankar delayed_extent = blockend; 135a1596438SUma Shankar delayed_skipfirst = skipfirst; 136a1596438SUma Shankar delayed_buf = buf; 137a1596438SUma Shankar delayed_next = blknr + 13850ce4c07SEgbert Eich (blockend >> log2blksz); 139a1596438SUma Shankar } 140a1596438SUma Shankar } else { 141a1596438SUma Shankar if (previous_block_number != -1) { 142a1596438SUma Shankar /* spill */ 143a1596438SUma Shankar status = ext4fs_devread(delayed_start, 144a1596438SUma Shankar delayed_skipfirst, 145a1596438SUma Shankar delayed_extent, 146a1596438SUma Shankar delayed_buf); 147a1596438SUma Shankar if (status == 0) 148a1596438SUma Shankar return -1; 149a1596438SUma Shankar previous_block_number = -1; 150a1596438SUma Shankar } 151a1596438SUma Shankar memset(buf, 0, blocksize - skipfirst); 152a1596438SUma Shankar } 153a1596438SUma Shankar buf += blocksize - skipfirst; 154a1596438SUma Shankar } 155a1596438SUma Shankar if (previous_block_number != -1) { 156a1596438SUma Shankar /* spill */ 157a1596438SUma Shankar status = ext4fs_devread(delayed_start, 158a1596438SUma Shankar delayed_skipfirst, delayed_extent, 159a1596438SUma Shankar delayed_buf); 160a1596438SUma Shankar if (status == 0) 161a1596438SUma Shankar return -1; 162a1596438SUma Shankar previous_block_number = -1; 163a1596438SUma Shankar } 164a1596438SUma Shankar 165a1596438SUma Shankar return len; 166a1596438SUma Shankar } 167a1596438SUma Shankar 168a1596438SUma Shankar int ext4fs_ls(const char *dirname) 169a1596438SUma Shankar { 170a1596438SUma Shankar struct ext2fs_node *dirnode; 171a1596438SUma Shankar int status; 172a1596438SUma Shankar 173a1596438SUma Shankar if (dirname == NULL) 174a1596438SUma Shankar return 0; 175a1596438SUma Shankar 176a1596438SUma Shankar status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode, 177a1596438SUma Shankar FILETYPE_DIRECTORY); 178a1596438SUma Shankar if (status != 1) { 179a1596438SUma Shankar printf("** Can not find directory. **\n"); 180a1596438SUma Shankar return 1; 181a1596438SUma Shankar } 182a1596438SUma Shankar 183a1596438SUma Shankar ext4fs_iterate_dir(dirnode, NULL, NULL, NULL); 184a1596438SUma Shankar ext4fs_free_node(dirnode, &ext4fs_root->diropen); 185a1596438SUma Shankar 186a1596438SUma Shankar return 0; 187a1596438SUma Shankar } 188a1596438SUma Shankar 189a1596438SUma Shankar int ext4fs_read(char *buf, unsigned len) 190a1596438SUma Shankar { 191a1596438SUma Shankar if (ext4fs_root == NULL || ext4fs_file == NULL) 192a1596438SUma Shankar return 0; 193a1596438SUma Shankar 194a1596438SUma Shankar return ext4fs_read_file(ext4fs_file, 0, len, buf); 195a1596438SUma Shankar } 196e6d52415SSimon Glass 197e6d52415SSimon Glass int ext4fs_probe(block_dev_desc_t *fs_dev_desc, 198e6d52415SSimon Glass disk_partition_t *fs_partition) 199e6d52415SSimon Glass { 200e6d52415SSimon Glass ext4fs_set_blk_dev(fs_dev_desc, fs_partition); 201e6d52415SSimon Glass 202e6d52415SSimon Glass if (!ext4fs_mount(fs_partition->size)) { 203e6d52415SSimon Glass ext4fs_close(); 204e6d52415SSimon Glass return -1; 205e6d52415SSimon Glass } 206e6d52415SSimon Glass 207e6d52415SSimon Glass return 0; 208e6d52415SSimon Glass } 209e6d52415SSimon Glass 210e6d52415SSimon Glass int ext4_read_file(const char *filename, void *buf, int offset, int len) 211e6d52415SSimon Glass { 212e6d52415SSimon Glass int file_len; 213e6d52415SSimon Glass int len_read; 214e6d52415SSimon Glass 215e6d52415SSimon Glass if (offset != 0) { 216e6d52415SSimon Glass printf("** Cannot support non-zero offset **\n"); 217e6d52415SSimon Glass return -1; 218e6d52415SSimon Glass } 219e6d52415SSimon Glass 220e6d52415SSimon Glass file_len = ext4fs_open(filename); 221e6d52415SSimon Glass if (file_len < 0) { 222e6d52415SSimon Glass printf("** File not found %s **\n", filename); 223e6d52415SSimon Glass return -1; 224e6d52415SSimon Glass } 225e6d52415SSimon Glass 226e6d52415SSimon Glass if (len == 0) 227e6d52415SSimon Glass len = file_len; 228e6d52415SSimon Glass 229e6d52415SSimon Glass len_read = ext4fs_read(buf, len); 230e6d52415SSimon Glass 231e6d52415SSimon Glass return len_read; 232e6d52415SSimon Glass } 233