Commit ed34f34d authored by Uma Shankar's avatar Uma Shankar Committed by Wolfgang Denk

ext4fs write support

Signed-off-by: default avatarUma Shankar <uma.shankar@samsung.com>
Signed-off-by: default avatarManjunatha C Achar <a.manjunatha@samsung.com>
Signed-off-by: default avatarIqbal Shareef <iqbal.ams@samsung.com>
Signed-off-by: default avatarHakgoo Lee <goodguy.lee@samsung.com>
parent a1596438
...@@ -62,6 +62,10 @@ ...@@ -62,6 +62,10 @@
uint64_t total_sector; uint64_t total_sector;
uint64_t part_offset; uint64_t part_offset;
#if defined(CONFIG_CMD_EXT4_WRITE)
static uint64_t part_size;
static uint16_t cur_part = 1;
#endif
#define DOS_PART_MAGIC_OFFSET 0x1fe #define DOS_PART_MAGIC_OFFSET 0x1fe
#define DOS_FS_TYPE_OFFSET 0x36 #define DOS_FS_TYPE_OFFSET 0x36
...@@ -84,6 +88,143 @@ int do_ext4_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ...@@ -84,6 +88,143 @@ int do_ext4_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
return 0; return 0;
} }
#if defined(CONFIG_CMD_EXT4_WRITE)
static int ext4_register_device(block_dev_desc_t *dev_desc, int part_no)
{
unsigned char buffer[SECTOR_SIZE];
disk_partition_t info;
if (!dev_desc->block_read)
return -1;
/* check if we have a MBR (on floppies we have only a PBR) */
if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *) buffer) != 1) {
printf("** Can't read from device %d **\n", dev_desc->dev);
return -1;
}
if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
/* no signature found */
return -1;
}
/* First we assume there is a MBR */
if (!get_partition_info(dev_desc, part_no, &info)) {
part_offset = info.start;
cur_part = part_no;
part_size = info.size;
} else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET],
"FAT", 3) == 0) || (strncmp((char *)&buffer
[DOS_FS32_TYPE_OFFSET],
"FAT32", 5) == 0)) {
/* ok, we assume we are on a PBR only */
cur_part = 1;
part_offset = 0;
} else {
printf("** Partition %d not valid on device %d **\n",
part_no, dev_desc->dev);
return -1;
}
return 0;
}
int do_ext4_write(cmd_tbl_t *cmdtp, int flag, int argc,
char *const argv[])
{
const char *filename = "/";
int part_length;
unsigned long part = 1;
int dev;
char *ep;
unsigned long ram_address;
unsigned long file_size;
disk_partition_t info;
struct ext_filesystem *fs;
if (argc < 6)
return cmd_usage(cmdtp);
dev = (int)simple_strtoul(argv[2], &ep, 16);
ext4_dev_desc = get_dev(argv[1], dev);
if (ext4_dev_desc == NULL) {
printf("Block device %s %d not supported\n", argv[1], dev);
return 1;
}
if (init_fs(ext4_dev_desc))
return 1;
fs = get_fs();
if (*ep) {
if (*ep != ':') {
puts("Invalid boot device, use `dev[:part]'\n");
goto fail;
}
part = simple_strtoul(++ep, NULL, 16);
}
/* get the filename */
filename = argv[3];
/* get the address in hexadecimal format (string to int) */
ram_address = simple_strtoul(argv[4], NULL, 16);
/* get the filesize in base 10 format */
file_size = simple_strtoul(argv[5], NULL, 10);
/* set the device as block device */
part_length = ext4fs_set_blk_dev(fs->dev_desc, part);
if (part_length == 0) {
printf("Bad partition - %s %d:%lu\n", argv[1], dev, part);
goto fail;
}
/* register the device and partition */
if (ext4_register_device(fs->dev_desc, part) != 0) {
printf("Unable to use %s %d:%lu for fattable\n",
argv[1], dev, part);
goto fail;
}
/* get the partition information */
if (!get_partition_info(fs->dev_desc, part, &info)) {
total_sector = (info.size * info.blksz) / SECTOR_SIZE;
fs->total_sect = total_sector;
} else {
printf("error : get partition info\n");
goto fail;
}
/* mount the filesystem */
if (!ext4fs_mount(part_length)) {
printf("Bad ext4 partition %s %d:%lu\n", argv[1], dev, part);
goto fail;
}
/* start write */
if (ext4fs_write(filename, (unsigned char *)ram_address, file_size)) {
printf("** Error ext4fs_write() **\n");
goto fail;
}
ext4fs_close();
deinit_fs(fs->dev_desc);
return 0;
fail:
ext4fs_close();
deinit_fs(fs->dev_desc);
return 1;
}
U_BOOT_CMD(ext4write, 6, 1, do_ext4_write,
"create a file in the root directory",
"<interface> <dev[:part]> [Absolute filename path] [Address] [sizebytes]\n"
" - create a file in / directory");
#endif
U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls, U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls,
"list files in a directory (default /)", "list files in a directory (default /)",
"<interface> <dev[:part]> [directory]\n" "<interface> <dev[:part]> [directory]\n"
......
This patch series adds support for ext4 ls,load and write features in uboot
Journaling is supported for write feature.
To Enable ext2 ls and load commands, modify the board specific config file with
#define CONFIG_CMD_EXT2
To Enable ext4 ls and load commands, modify the board specific config file with
#define CONFIG_CMD_EXT4
To enable ext4 write command, modify the board specific config file with
#define CONFIG_CMD_EXT4
#define CONFIG_CMD_EXT4_WRITE
Steps to test:
1. After applying the patch, ext4 specific commands can be seen
in the boot loader prompt using
UBOOT #help
ext4load- load binary file from a Ext4 file system
ext4ls - list files in a directory (default /)
ext4write- create a file in ext4 formatted partition
2. To list the files in ext4 formatted partition, execute
ext4ls <interface> <dev[:part]> [directory]
For example:
UBOOT #ext4ls mmc 0:5 /usr/lib
3. To read and load a file from an ext4 formatted partition to RAM, execute
ext4load <interface> <dev[:part]> [addr] [filename] [bytes]
For example:
UBOOT #ext4load mmc 2:2 0x30007fc0 uImage
4. To write a file to a ext4 formatted partition.
a) First load a file to RAM at a particular address for example 0x30007fc0.
Now execute ext4write command
ext4write <interface> <dev[:part]> [filename] [Address] [sizebytes]
For example:
UBOOT #ext4write mmc 2:2 /boot/uImage 0x30007fc0 6183120
(here 6183120 is the size of the file to be written)
Note: Absolute path is required for the file to be written
References :
-- ext4 implementation in Linux Kernel
-- Uboot existing ext2 load and ls implementation
-- Journaling block device JBD2 implementation in linux Kernel
...@@ -34,6 +34,7 @@ COBJS-$(CONFIG_CMD_EXT4) := ext4fs.o ext4_common.o dev.o ...@@ -34,6 +34,7 @@ COBJS-$(CONFIG_CMD_EXT4) := ext4fs.o ext4_common.o dev.o
ifndef CONFIG_CMD_EXT4 ifndef CONFIG_CMD_EXT4
COBJS-$(CONFIG_CMD_EXT2) := ext4fs.o ext4_common.o dev.o COBJS-$(CONFIG_CMD_EXT2) := ext4fs.o ext4_common.o dev.o
endif endif
COBJS-$(CONFIG_CMD_EXT4_WRITE) += ext4_journal.o crc16.o
SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c) SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS-y)) OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS-y))
......
/*
* crc16.c
*
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
*/
#include <common.h>
#include <asm/byteorder.h>
#include <linux/stat.h>
#include "crc16.h"
/** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */
static __u16 const crc16_table[256] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
/**
* Compute the CRC-16 for the data buffer
*/
unsigned int ext2fs_crc16(unsigned int crc,
const void *buffer, unsigned int len)
{
const unsigned char *cp = buffer;
while (len--)
crc = (((crc >> 8) & 0xffU) ^
crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
return crc;
}
/*
* crc16.h - CRC-16 routine
* Implements the standard CRC-16:
* Width 16
* Poly 0x8005 (x16 + x15 + x2 + 1)
* Init 0
*
* Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
*/
#ifndef __CRC16_H
#define __CRC16_H
extern unsigned int ext2fs_crc16(unsigned int crc,
const void *buffer, unsigned int len);
#endif
This diff is collapsed.
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
* *
* ext4write : Based on generic ext4 protocol.
*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
...@@ -35,6 +37,10 @@ ...@@ -35,6 +37,10 @@
#include <ext4fs.h> #include <ext4fs.h>
#include <malloc.h> #include <malloc.h>
#include <asm/errno.h> #include <asm/errno.h>
#if defined(CONFIG_CMD_EXT4_WRITE)
#include "ext4_journal.h"
#include "crc16.h"
#endif
#define YES 1 #define YES 1
#define NO 0 #define NO 0
...@@ -60,4 +66,23 @@ int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode, ...@@ -60,4 +66,23 @@ int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode,
struct ext2fs_node **foundnode, int expecttype); struct ext2fs_node **foundnode, int expecttype);
int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
struct ext2fs_node **fnode, int *ftype); struct ext2fs_node **fnode, int *ftype);
#if defined(CONFIG_CMD_EXT4_WRITE)
uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n);
int ext4fs_checksum_update(unsigned int i);
int ext4fs_get_parent_inode_num(const char *dirname, char *dname, int flags);
void ext4fs_update_parent_dentry(char *filename, int *p_ino, int file_type);
long int ext4fs_get_new_blk_no(void);
int ext4fs_get_new_inode_no(void);
void ext4fs_reset_block_bmap(long int blockno, unsigned char *buffer,
int index);
int ext4fs_set_block_bmap(long int blockno, unsigned char *buffer, int index);
int ext4fs_set_inode_bmap(int inode_no, unsigned char *buffer, int index);
void ext4fs_reset_inode_bmap(int inode_no, unsigned char *buffer, int index);
int ext4fs_iget(int inode_no, struct ext2_inode *inode);
void ext4fs_allocate_blocks(struct ext2_inode *file_inode,
unsigned int total_remaining_blocks,
unsigned int *total_no_of_block);
void put_ext4(uint64_t off, void *buf, uint32_t size);
#endif
#endif #endif
/*
* (C) Copyright 2011 - 2012 Samsung Electronics
* EXT4 filesystem implementation in Uboot by
* Uma Shankar <uma.shankar@samsung.com>
* Manjunatha C Achar <a.manjunatha@samsung.com>
*
* Journal data structures and headers for Journaling feature of ext4
* have been referred from JBD2 (Journaling Block device 2)
* implementation in Linux Kernel.
* Written by Stephen C. Tweedie <sct@redhat.com>
*
* Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
* option, any later version, incorporated herein by reference.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common.h>
#include <ext4fs.h>
#include <malloc.h>
#include <ext_common.h>
#include "ext4_common.h"
static struct revoke_blk_list *revk_blk_list;
static struct revoke_blk_list *prev_node;
static int first_node = TRUE;
int gindex;
int gd_index;
int jrnl_blk_idx;
struct journal_log *journal_ptr[MAX_JOURNAL_ENTRIES];
struct dirty_blocks *dirty_block_ptr[MAX_JOURNAL_ENTRIES];
int ext4fs_init_journal(void)
{
int i;
char *temp = NULL;
struct ext_filesystem *fs = get_fs();
/* init globals */
revk_blk_list = NULL;
prev_node = NULL;
gindex = 0;
gd_index = 0;
jrnl_blk_idx = 1;
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
journal_ptr[i] = zalloc(sizeof(struct journal_log));
if (!journal_ptr[i])
goto fail;
dirty_block_ptr[i] = zalloc(sizeof(struct dirty_blocks));
if (!dirty_block_ptr[i])
goto fail;
journal_ptr[i]->buf = NULL;
journal_ptr[i]->blknr = -1;
dirty_block_ptr[i]->buf = NULL;
dirty_block_ptr[i]->blknr = -1;
}
if (fs->blksz == 4096) {
temp = zalloc(fs->blksz);
if (!temp)
goto fail;
journal_ptr[gindex]->buf = zalloc(fs->blksz);
if (!journal_ptr[gindex]->buf)
goto fail;
ext4fs_devread(0, 0, fs->blksz, temp);
memcpy(temp + SUPERBLOCK_SIZE, fs->sb, SUPERBLOCK_SIZE);
memcpy(journal_ptr[gindex]->buf, temp, fs->blksz);
journal_ptr[gindex++]->blknr = 0;
free(temp);
} else {
journal_ptr[gindex]->buf = zalloc(fs->blksz);
if (!journal_ptr[gindex]->buf)
goto fail;
memcpy(journal_ptr[gindex]->buf, fs->sb, SUPERBLOCK_SIZE);
journal_ptr[gindex++]->blknr = 1;
}
/* Check the file system state using journal super block */
if (ext4fs_check_journal_state(SCAN))
goto fail;
/* Check the file system state using journal super block */
if (ext4fs_check_journal_state(RECOVER))
goto fail;
return 0;
fail:
return -1;
}
void ext4fs_dump_metadata(void)
{
struct ext_filesystem *fs = get_fs();
int i;
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (dirty_block_ptr[i]->blknr == -1)
break;
put_ext4((uint64_t) ((uint64_t)dirty_block_ptr[i]->blknr *
(uint64_t)fs->blksz), dirty_block_ptr[i]->buf,
fs->blksz);
}
}
void ext4fs_free_journal(void)
{
int i;
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (dirty_block_ptr[i]->blknr == -1)
break;
if (dirty_block_ptr[i]->buf)
free(dirty_block_ptr[i]->buf);
}
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (journal_ptr[i]->blknr == -1)
break;
if (journal_ptr[i]->buf)
free(journal_ptr[i]->buf);
}
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (journal_ptr[i])
free(journal_ptr[i]);
if (dirty_block_ptr[i])
free(dirty_block_ptr[i]);
}
gindex = 0;
gd_index = 0;
jrnl_blk_idx = 1;
}
int ext4fs_log_gdt(char *gd_table)
{
struct ext_filesystem *fs = get_fs();
short i;
long int var = fs->gdtable_blkno;
for (i = 0; i < fs->no_blk_pergdt; i++) {
journal_ptr[gindex]->buf = zalloc(fs->blksz);
if (!journal_ptr[gindex]->buf)
return -ENOMEM;
memcpy(journal_ptr[gindex]->buf, gd_table, fs->blksz);
gd_table += fs->blksz;
journal_ptr[gindex++]->blknr = var++;
}
return 0;
}
/*
* This function stores the backup copy of meta data in RAM
* journal_buffer -- Buffer containing meta data
* blknr -- Block number on disk of the meta data buffer
*/
int ext4fs_log_journal(char *journal_buffer, long int blknr)
{
struct ext_filesystem *fs = get_fs();
short i;
if (!journal_buffer) {
printf("Invalid input arguments %s\n", __func__);
return -EINVAL;
}
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (journal_ptr[i]->blknr == -1)
break;
if (journal_ptr[i]->blknr == blknr)
return 0;
}
journal_ptr[gindex]->buf = zalloc(fs->blksz);
if (!journal_ptr[gindex]->buf)
return -ENOMEM;
memcpy(journal_ptr[gindex]->buf, journal_buffer, fs->blksz);
journal_ptr[gindex++]->blknr = blknr;
return 0;
}
/*
* This function stores the modified meta data in RAM
* metadata_buffer -- Buffer containing meta data
* blknr -- Block number on disk of the meta data buffer
*/
int ext4fs_put_metadata(char *metadata_buffer, long int blknr)
{
struct ext_filesystem *fs = get_fs();
if (!metadata_buffer) {
printf("Invalid input arguments %s\n", __func__);
return -EINVAL;
}
dirty_block_ptr[gd_index]->buf = zalloc(fs->blksz);
if (!dirty_block_ptr[gd_index]->buf)
return -ENOMEM;
memcpy(dirty_block_ptr[gd_index]->buf, metadata_buffer, fs->blksz);
dirty_block_ptr[gd_index++]->blknr = blknr;
return 0;
}
void print_revoke_blks(char *revk_blk)
{
int offset;
int max;
long int blocknr;
struct journal_revoke_header_t *header;
if (revk_blk == NULL)
return;
header = (struct journal_revoke_header_t *) revk_blk;
offset = sizeof(struct journal_revoke_header_t);
max = be32_to_cpu(header->r_count);
printf("total bytes %d\n", max);
while (offset < max) {
blocknr = be32_to_cpu(*((long int *)(revk_blk + offset)));
printf("revoke blknr is %ld\n", blocknr);
offset += 4;
}
}
static struct revoke_blk_list *_get_node(void)
{
struct revoke_blk_list *tmp_node;
tmp_node = zalloc(sizeof(struct revoke_blk_list));
if (tmp_node == NULL)
return NULL;
tmp_node->content = NULL;
tmp_node->next = NULL;
return tmp_node;
}
void ext4fs_push_revoke_blk(char *buffer)
{
struct revoke_blk_list *node = NULL;
struct ext_filesystem *fs = get_fs();
if (buffer == NULL) {
printf("buffer ptr is NULL\n");
return;
}
node = _get_node();
<