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 * 211a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 22a1596438SUma Shankar */ 23a1596438SUma Shankar 24a1596438SUma Shankar #include <common.h> 25a1596438SUma Shankar #include <ext_common.h> 26a1596438SUma Shankar #include <ext4fs.h> 27a1596438SUma Shankar #include "ext4_common.h" 28a1596438SUma Shankar 29a1596438SUma Shankar int ext4fs_symlinknest; 3094501062SRob Herring struct ext_filesystem ext_fs; 31a1596438SUma Shankar 32a1596438SUma Shankar struct ext_filesystem *get_fs(void) 33a1596438SUma Shankar { 3494501062SRob Herring return &ext_fs; 35a1596438SUma Shankar } 36a1596438SUma Shankar 37a1596438SUma Shankar void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot) 38a1596438SUma Shankar { 39a1596438SUma Shankar if ((node != &ext4fs_root->diropen) && (node != currroot)) 40a1596438SUma Shankar free(node); 41a1596438SUma Shankar } 42a1596438SUma Shankar 43a1596438SUma Shankar /* 44a1596438SUma Shankar * Taken from openmoko-kernel mailing list: By Andy green 45a1596438SUma Shankar * Optimized read file API : collects and defers contiguous sector 46a1596438SUma Shankar * reads into one potentially more efficient larger sequential read action 47a1596438SUma Shankar */ 48a1596438SUma Shankar int ext4fs_read_file(struct ext2fs_node *node, int pos, 49a1596438SUma Shankar unsigned int len, char *buf) 50a1596438SUma Shankar { 5150ce4c07SEgbert Eich struct ext_filesystem *fs = get_fs(); 52a1596438SUma Shankar int i; 5304735e9cSFrederic Leroy lbaint_t blockcnt; 5450ce4c07SEgbert Eich int log2blksz = fs->dev_desc->log2blksz; 5550ce4c07SEgbert Eich int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz; 5650ce4c07SEgbert Eich int blocksize = (1 << (log2_fs_blocksize + log2blksz)); 57a1596438SUma Shankar unsigned int filesize = __le32_to_cpu(node->inode.size); 5804735e9cSFrederic Leroy lbaint_t previous_block_number = -1; 5904735e9cSFrederic Leroy lbaint_t delayed_start = 0; 6004735e9cSFrederic Leroy lbaint_t delayed_extent = 0; 6104735e9cSFrederic Leroy lbaint_t delayed_skipfirst = 0; 6204735e9cSFrederic Leroy lbaint_t delayed_next = 0; 63a1596438SUma Shankar char *delayed_buf = NULL; 64a1596438SUma Shankar short status; 65a1596438SUma Shankar 66a1596438SUma Shankar /* Adjust len so it we can't read past the end of the file. */ 67a1596438SUma Shankar if (len > filesize) 68a1596438SUma Shankar len = filesize; 69a1596438SUma Shankar 70a1596438SUma Shankar blockcnt = ((len + pos) + blocksize - 1) / blocksize; 71a1596438SUma Shankar 72a1596438SUma Shankar for (i = pos / blocksize; i < blockcnt; i++) { 7304735e9cSFrederic Leroy lbaint_t blknr; 74a1596438SUma Shankar int blockoff = pos % blocksize; 75a1596438SUma Shankar int blockend = blocksize; 76a1596438SUma Shankar int skipfirst = 0; 77a1596438SUma Shankar blknr = read_allocated_block(&(node->inode), i); 78*715b56feSTom Rini if (blknr < 0) 79*715b56feSTom Rini return -1; 80a1596438SUma Shankar 8150ce4c07SEgbert Eich blknr = blknr << log2_fs_blocksize; 82a1596438SUma Shankar 83a1596438SUma Shankar /* Last block. */ 84a1596438SUma Shankar if (i == blockcnt - 1) { 85a1596438SUma Shankar blockend = (len + pos) % blocksize; 86a1596438SUma Shankar 87a1596438SUma Shankar /* The last portion is exactly blocksize. */ 88a1596438SUma Shankar if (!blockend) 89a1596438SUma Shankar blockend = blocksize; 90a1596438SUma Shankar } 91a1596438SUma Shankar 92a1596438SUma Shankar /* First block. */ 93a1596438SUma Shankar if (i == pos / blocksize) { 94a1596438SUma Shankar skipfirst = blockoff; 95a1596438SUma Shankar blockend -= skipfirst; 96a1596438SUma Shankar } 97a1596438SUma Shankar if (blknr) { 98a1596438SUma Shankar int status; 99a1596438SUma Shankar 100a1596438SUma Shankar if (previous_block_number != -1) { 101a1596438SUma Shankar if (delayed_next == blknr) { 102a1596438SUma Shankar delayed_extent += blockend; 10350ce4c07SEgbert Eich delayed_next += blockend >> log2blksz; 104a1596438SUma Shankar } else { /* spill */ 105a1596438SUma Shankar status = ext4fs_devread(delayed_start, 106a1596438SUma Shankar delayed_skipfirst, 107a1596438SUma Shankar delayed_extent, 108a1596438SUma Shankar delayed_buf); 109*715b56feSTom Rini if (status == 0) 110*715b56feSTom Rini return -1; 111a1596438SUma Shankar previous_block_number = blknr; 112a1596438SUma Shankar delayed_start = blknr; 113a1596438SUma Shankar delayed_extent = blockend; 114a1596438SUma Shankar delayed_skipfirst = skipfirst; 115a1596438SUma Shankar delayed_buf = buf; 116a1596438SUma Shankar delayed_next = blknr + 11750ce4c07SEgbert Eich (blockend >> log2blksz); 118a1596438SUma Shankar } 119a1596438SUma Shankar } else { 120a1596438SUma Shankar previous_block_number = blknr; 121a1596438SUma Shankar delayed_start = blknr; 122a1596438SUma Shankar delayed_extent = blockend; 123a1596438SUma Shankar delayed_skipfirst = skipfirst; 124a1596438SUma Shankar delayed_buf = buf; 125a1596438SUma Shankar delayed_next = blknr + 12650ce4c07SEgbert Eich (blockend >> log2blksz); 127a1596438SUma Shankar } 128a1596438SUma Shankar } else { 129a1596438SUma Shankar if (previous_block_number != -1) { 130a1596438SUma Shankar /* spill */ 131a1596438SUma Shankar status = ext4fs_devread(delayed_start, 132a1596438SUma Shankar delayed_skipfirst, 133a1596438SUma Shankar delayed_extent, 134a1596438SUma Shankar delayed_buf); 135*715b56feSTom Rini if (status == 0) 136*715b56feSTom Rini return -1; 137a1596438SUma Shankar previous_block_number = -1; 138a1596438SUma Shankar } 139a1596438SUma Shankar memset(buf, 0, blocksize - skipfirst); 140a1596438SUma Shankar } 141a1596438SUma Shankar buf += blocksize - skipfirst; 142a1596438SUma Shankar } 143a1596438SUma Shankar if (previous_block_number != -1) { 144a1596438SUma Shankar /* spill */ 145a1596438SUma Shankar status = ext4fs_devread(delayed_start, 146a1596438SUma Shankar delayed_skipfirst, delayed_extent, 147a1596438SUma Shankar delayed_buf); 148*715b56feSTom Rini if (status == 0) 149*715b56feSTom Rini return -1; 150a1596438SUma Shankar previous_block_number = -1; 151a1596438SUma Shankar } 152a1596438SUma Shankar 153a1596438SUma Shankar return len; 154a1596438SUma Shankar } 155a1596438SUma Shankar 156a1596438SUma Shankar int ext4fs_ls(const char *dirname) 157a1596438SUma Shankar { 158a1596438SUma Shankar struct ext2fs_node *dirnode; 159a1596438SUma Shankar int status; 160a1596438SUma Shankar 161a1596438SUma Shankar if (dirname == NULL) 162a1596438SUma Shankar return 0; 163a1596438SUma Shankar 164a1596438SUma Shankar status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode, 165a1596438SUma Shankar FILETYPE_DIRECTORY); 166a1596438SUma Shankar if (status != 1) { 167a1596438SUma Shankar printf("** Can not find directory. **\n"); 168a1596438SUma Shankar return 1; 169a1596438SUma Shankar } 170a1596438SUma Shankar 171a1596438SUma Shankar ext4fs_iterate_dir(dirnode, NULL, NULL, NULL); 172a1596438SUma Shankar ext4fs_free_node(dirnode, &ext4fs_root->diropen); 173a1596438SUma Shankar 174a1596438SUma Shankar return 0; 175a1596438SUma Shankar } 176a1596438SUma Shankar 17755af5c93SStephen Warren int ext4fs_exists(const char *filename) 17855af5c93SStephen Warren { 17955af5c93SStephen Warren int file_len; 18055af5c93SStephen Warren 18155af5c93SStephen Warren file_len = ext4fs_open(filename); 18255af5c93SStephen Warren return file_len >= 0; 18355af5c93SStephen Warren } 18455af5c93SStephen Warren 185a1596438SUma Shankar int ext4fs_read(char *buf, unsigned len) 186a1596438SUma Shankar { 187a1596438SUma Shankar if (ext4fs_root == NULL || ext4fs_file == NULL) 188a1596438SUma Shankar return 0; 189a1596438SUma Shankar 190a1596438SUma Shankar return ext4fs_read_file(ext4fs_file, 0, len, buf); 191a1596438SUma Shankar } 192e6d52415SSimon Glass 193e6d52415SSimon Glass int ext4fs_probe(block_dev_desc_t *fs_dev_desc, 194e6d52415SSimon Glass disk_partition_t *fs_partition) 195e6d52415SSimon Glass { 196e6d52415SSimon Glass ext4fs_set_blk_dev(fs_dev_desc, fs_partition); 197e6d52415SSimon Glass 198e6d52415SSimon Glass if (!ext4fs_mount(fs_partition->size)) { 199e6d52415SSimon Glass ext4fs_close(); 200e6d52415SSimon Glass return -1; 201e6d52415SSimon Glass } 202e6d52415SSimon Glass 203e6d52415SSimon Glass return 0; 204e6d52415SSimon Glass } 205e6d52415SSimon Glass 206e6d52415SSimon Glass int ext4_read_file(const char *filename, void *buf, int offset, int len) 207e6d52415SSimon Glass { 208e6d52415SSimon Glass int file_len; 209e6d52415SSimon Glass int len_read; 210e6d52415SSimon Glass 211e6d52415SSimon Glass if (offset != 0) { 212e6d52415SSimon Glass printf("** Cannot support non-zero offset **\n"); 213e6d52415SSimon Glass return -1; 214e6d52415SSimon Glass } 215e6d52415SSimon Glass 216e6d52415SSimon Glass file_len = ext4fs_open(filename); 217e6d52415SSimon Glass if (file_len < 0) { 218e6d52415SSimon Glass printf("** File not found %s **\n", filename); 219e6d52415SSimon Glass return -1; 220e6d52415SSimon Glass } 221e6d52415SSimon Glass 222e6d52415SSimon Glass if (len == 0) 223e6d52415SSimon Glass len = file_len; 224e6d52415SSimon Glass 225e6d52415SSimon Glass len_read = ext4fs_read(buf, len); 226e6d52415SSimon Glass 227e6d52415SSimon Glass return len_read; 228e6d52415SSimon Glass } 229