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" 289e374e7bSTom Rini #include <div64.h> 29a1596438SUma Shankar 30a1596438SUma Shankar int ext4fs_symlinknest; 3194501062SRob Herring struct ext_filesystem ext_fs; 32a1596438SUma Shankar 33a1596438SUma Shankar struct ext_filesystem *get_fs(void) 34a1596438SUma Shankar { 3594501062SRob Herring return &ext_fs; 36a1596438SUma Shankar } 37a1596438SUma Shankar 38a1596438SUma Shankar void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot) 39a1596438SUma Shankar { 40a1596438SUma Shankar if ((node != &ext4fs_root->diropen) && (node != currroot)) 41a1596438SUma Shankar free(node); 42a1596438SUma Shankar } 43a1596438SUma Shankar 44a1596438SUma Shankar /* 45a1596438SUma Shankar * Taken from openmoko-kernel mailing list: By Andy green 46a1596438SUma Shankar * Optimized read file API : collects and defers contiguous sector 47a1596438SUma Shankar * reads into one potentially more efficient larger sequential read action 48a1596438SUma Shankar */ 499f12cd0eSSuriyan Ramasami int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, 509f12cd0eSSuriyan Ramasami loff_t len, char *buf, loff_t *actread) 51a1596438SUma Shankar { 5250ce4c07SEgbert Eich struct ext_filesystem *fs = get_fs(); 53a1596438SUma Shankar int i; 5404735e9cSFrederic Leroy lbaint_t blockcnt; 5550ce4c07SEgbert Eich int log2blksz = fs->dev_desc->log2blksz; 5650ce4c07SEgbert Eich int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz; 5750ce4c07SEgbert Eich int blocksize = (1 << (log2_fs_blocksize + log2blksz)); 587f101be3SMichael Walle unsigned int filesize = le32_to_cpu(node->inode.size); 5904735e9cSFrederic Leroy lbaint_t previous_block_number = -1; 6004735e9cSFrederic Leroy lbaint_t delayed_start = 0; 6104735e9cSFrederic Leroy lbaint_t delayed_extent = 0; 6204735e9cSFrederic Leroy lbaint_t delayed_skipfirst = 0; 6304735e9cSFrederic Leroy lbaint_t delayed_next = 0; 64a1596438SUma Shankar char *delayed_buf = NULL; 65a1596438SUma Shankar short status; 66a1596438SUma Shankar 67*ecdfb419SIan Ray if (blocksize <= 0) 68*ecdfb419SIan Ray return -1; 69*ecdfb419SIan Ray 70a1596438SUma Shankar /* Adjust len so it we can't read past the end of the file. */ 7166a47ff2SStefan Brüns if (len + pos > filesize) 7266a47ff2SStefan Brüns len = (filesize - pos); 73a1596438SUma Shankar 749e374e7bSTom Rini blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize); 75a1596438SUma Shankar 769e374e7bSTom Rini for (i = lldiv(pos, blocksize); i < blockcnt; i++) { 77509b498aSLokesh Vutla long int blknr; 789e374e7bSTom Rini int blockoff = pos - (blocksize * i); 79a1596438SUma Shankar int blockend = blocksize; 80a1596438SUma Shankar int skipfirst = 0; 81a1596438SUma Shankar blknr = read_allocated_block(&(node->inode), i); 82715b56feSTom Rini if (blknr < 0) 83715b56feSTom Rini return -1; 84a1596438SUma Shankar 8550ce4c07SEgbert Eich blknr = blknr << log2_fs_blocksize; 86a1596438SUma Shankar 87a1596438SUma Shankar /* Last block. */ 88a1596438SUma Shankar if (i == blockcnt - 1) { 899e374e7bSTom Rini blockend = (len + pos) - (blocksize * i); 90a1596438SUma Shankar 91a1596438SUma Shankar /* The last portion is exactly blocksize. */ 92a1596438SUma Shankar if (!blockend) 93a1596438SUma Shankar blockend = blocksize; 94a1596438SUma Shankar } 95a1596438SUma Shankar 96a1596438SUma Shankar /* First block. */ 979e374e7bSTom Rini if (i == lldiv(pos, blocksize)) { 98a1596438SUma Shankar skipfirst = blockoff; 99a1596438SUma Shankar blockend -= skipfirst; 100a1596438SUma Shankar } 101a1596438SUma Shankar if (blknr) { 102a1596438SUma Shankar int status; 103a1596438SUma Shankar 104a1596438SUma Shankar if (previous_block_number != -1) { 105a1596438SUma Shankar if (delayed_next == blknr) { 106a1596438SUma Shankar delayed_extent += blockend; 10750ce4c07SEgbert Eich delayed_next += blockend >> log2blksz; 108a1596438SUma Shankar } else { /* spill */ 109a1596438SUma Shankar status = ext4fs_devread(delayed_start, 110a1596438SUma Shankar delayed_skipfirst, 111a1596438SUma Shankar delayed_extent, 112a1596438SUma Shankar delayed_buf); 113715b56feSTom Rini if (status == 0) 114715b56feSTom Rini return -1; 115a1596438SUma Shankar previous_block_number = blknr; 116a1596438SUma Shankar delayed_start = blknr; 117a1596438SUma Shankar delayed_extent = blockend; 118a1596438SUma Shankar delayed_skipfirst = skipfirst; 119a1596438SUma Shankar delayed_buf = buf; 120a1596438SUma Shankar delayed_next = blknr + 12150ce4c07SEgbert Eich (blockend >> log2blksz); 122a1596438SUma Shankar } 123a1596438SUma Shankar } else { 124a1596438SUma Shankar previous_block_number = blknr; 125a1596438SUma Shankar delayed_start = blknr; 126a1596438SUma Shankar delayed_extent = blockend; 127a1596438SUma Shankar delayed_skipfirst = skipfirst; 128a1596438SUma Shankar delayed_buf = buf; 129a1596438SUma Shankar delayed_next = blknr + 13050ce4c07SEgbert Eich (blockend >> log2blksz); 131a1596438SUma Shankar } 132a1596438SUma Shankar } else { 133*ecdfb419SIan Ray int n; 134a1596438SUma Shankar if (previous_block_number != -1) { 135a1596438SUma Shankar /* spill */ 136a1596438SUma Shankar status = ext4fs_devread(delayed_start, 137a1596438SUma Shankar delayed_skipfirst, 138a1596438SUma Shankar delayed_extent, 139a1596438SUma Shankar delayed_buf); 140715b56feSTom Rini if (status == 0) 141715b56feSTom Rini return -1; 142a1596438SUma Shankar previous_block_number = -1; 143a1596438SUma Shankar } 144*ecdfb419SIan Ray /* Zero no more than `len' bytes. */ 145*ecdfb419SIan Ray n = blocksize - skipfirst; 146*ecdfb419SIan Ray if (n > len) 147*ecdfb419SIan Ray n = len; 148*ecdfb419SIan Ray memset(buf, 0, n); 149a1596438SUma Shankar } 150a1596438SUma Shankar buf += blocksize - skipfirst; 151a1596438SUma Shankar } 152a1596438SUma Shankar if (previous_block_number != -1) { 153a1596438SUma Shankar /* spill */ 154a1596438SUma Shankar status = ext4fs_devread(delayed_start, 155a1596438SUma Shankar delayed_skipfirst, delayed_extent, 156a1596438SUma Shankar delayed_buf); 157715b56feSTom Rini if (status == 0) 158715b56feSTom Rini return -1; 159a1596438SUma Shankar previous_block_number = -1; 160a1596438SUma Shankar } 161a1596438SUma Shankar 1629f12cd0eSSuriyan Ramasami *actread = len; 1639f12cd0eSSuriyan Ramasami return 0; 164a1596438SUma Shankar } 165a1596438SUma Shankar 166a1596438SUma Shankar int ext4fs_ls(const char *dirname) 167a1596438SUma Shankar { 168a1596438SUma Shankar struct ext2fs_node *dirnode; 169a1596438SUma Shankar int status; 170a1596438SUma Shankar 171a1596438SUma Shankar if (dirname == NULL) 172a1596438SUma Shankar return 0; 173a1596438SUma Shankar 174a1596438SUma Shankar status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode, 175a1596438SUma Shankar FILETYPE_DIRECTORY); 176a1596438SUma Shankar if (status != 1) { 177a1596438SUma Shankar printf("** Can not find directory. **\n"); 178fa9ca8a5STom Rini ext4fs_free_node(dirnode, &ext4fs_root->diropen); 179a1596438SUma Shankar return 1; 180a1596438SUma Shankar } 181a1596438SUma Shankar 182a1596438SUma Shankar ext4fs_iterate_dir(dirnode, NULL, NULL, NULL); 183a1596438SUma Shankar ext4fs_free_node(dirnode, &ext4fs_root->diropen); 184a1596438SUma Shankar 185a1596438SUma Shankar return 0; 186a1596438SUma Shankar } 187a1596438SUma Shankar 18855af5c93SStephen Warren int ext4fs_exists(const char *filename) 18955af5c93SStephen Warren { 1909f12cd0eSSuriyan Ramasami loff_t file_len; 1919f12cd0eSSuriyan Ramasami int ret; 19255af5c93SStephen Warren 1939f12cd0eSSuriyan Ramasami ret = ext4fs_open(filename, &file_len); 1949f12cd0eSSuriyan Ramasami return ret == 0; 19555af5c93SStephen Warren } 19655af5c93SStephen Warren 197d455d878SSuriyan Ramasami int ext4fs_size(const char *filename, loff_t *size) 198cf659819SStephen Warren { 199d455d878SSuriyan Ramasami return ext4fs_open(filename, size); 200cf659819SStephen Warren } 201cf659819SStephen Warren 20266a47ff2SStefan Brüns int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread) 203a1596438SUma Shankar { 204a1596438SUma Shankar if (ext4fs_root == NULL || ext4fs_file == NULL) 20566a47ff2SStefan Brüns return -1; 206a1596438SUma Shankar 20766a47ff2SStefan Brüns return ext4fs_read_file(ext4fs_file, offset, len, buf, actread); 208a1596438SUma Shankar } 209e6d52415SSimon Glass 2104101f687SSimon Glass int ext4fs_probe(struct blk_desc *fs_dev_desc, 211e6d52415SSimon Glass disk_partition_t *fs_partition) 212e6d52415SSimon Glass { 213e6d52415SSimon Glass ext4fs_set_blk_dev(fs_dev_desc, fs_partition); 214e6d52415SSimon Glass 215e6d52415SSimon Glass if (!ext4fs_mount(fs_partition->size)) { 216e6d52415SSimon Glass ext4fs_close(); 217e6d52415SSimon Glass return -1; 218e6d52415SSimon Glass } 219e6d52415SSimon Glass 220e6d52415SSimon Glass return 0; 221e6d52415SSimon Glass } 222e6d52415SSimon Glass 223d455d878SSuriyan Ramasami int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len, 224d455d878SSuriyan Ramasami loff_t *len_read) 225e6d52415SSimon Glass { 2269f12cd0eSSuriyan Ramasami loff_t file_len; 2279f12cd0eSSuriyan Ramasami int ret; 228e6d52415SSimon Glass 2299f12cd0eSSuriyan Ramasami ret = ext4fs_open(filename, &file_len); 2309f12cd0eSSuriyan Ramasami if (ret < 0) { 231e6d52415SSimon Glass printf("** File not found %s **\n", filename); 232e6d52415SSimon Glass return -1; 233e6d52415SSimon Glass } 234e6d52415SSimon Glass 235e6d52415SSimon Glass if (len == 0) 236e6d52415SSimon Glass len = file_len; 237e6d52415SSimon Glass 23866a47ff2SStefan Brüns return ext4fs_read(buf, offset, len, len_read); 239e6d52415SSimon Glass } 24059e890efSChristian Gmeiner 24159e890efSChristian Gmeiner int ext4fs_uuid(char *uuid_str) 24259e890efSChristian Gmeiner { 24359e890efSChristian Gmeiner if (ext4fs_root == NULL) 24459e890efSChristian Gmeiner return -1; 24559e890efSChristian Gmeiner 24659e890efSChristian Gmeiner #ifdef CONFIG_LIB_UUID 24759e890efSChristian Gmeiner uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id, 24859e890efSChristian Gmeiner uuid_str, UUID_STR_FORMAT_STD); 24959e890efSChristian Gmeiner 25059e890efSChristian Gmeiner return 0; 25159e890efSChristian Gmeiner #else 25259e890efSChristian Gmeiner return -ENOSYS; 25359e890efSChristian Gmeiner #endif 25459e890efSChristian Gmeiner } 255