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 @@
uint64_t total_sector;
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_FS_TYPE_OFFSET 0x36
......@@ -84,6 +88,143 @@ int do_ext4_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
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,
"list files in a directory (default /)",
"<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
ifndef CONFIG_CMD_EXT4
COBJS-$(CONFIG_CMD_EXT2) := ext4fs.o ext4_common.o dev.o
endif
COBJS-$(CONFIG_CMD_EXT4_WRITE) += ext4_journal.o crc16.o
SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c)
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
......@@ -14,6 +14,8 @@
* GRUB -- GRand Unified Bootloader
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
......@@ -54,6 +56,1357 @@ int ext4fs_indir3_blkno = -1;
struct ext2_inode *g_parent_inode;
static int symlinknest;
#if defined(CONFIG_CMD_EXT4_WRITE)
uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n)
{
uint32_t res = size / n;
if (res * n != size)
res++;
return res;
}
void put_ext4(uint64_t off, void *buf, uint32_t size)
{
uint64_t startblock;
uint64_t remainder;
unsigned char *temp_ptr = NULL;
unsigned char sec_buf[SECTOR_SIZE];
struct ext_filesystem *fs = get_fs();
startblock = off / (uint64_t)SECTOR_SIZE;
startblock += part_offset;
remainder = off % (uint64_t)SECTOR_SIZE;
remainder &= SECTOR_SIZE - 1;
if (fs->dev_desc == NULL)
return;
if ((startblock + (size / SECTOR_SIZE)) >
(part_offset + fs->total_sect)) {
printf("part_offset is %lu\n", part_offset);
printf("total_sector is %llu\n", fs->total_sect);
printf("error: overflow occurs\n");
return;
}
if (remainder) {
if (fs->dev_desc->block_read) {
fs->dev_desc->block_read(fs->dev_desc->dev,
startblock, 1, sec_buf);
temp_ptr = sec_buf;
memcpy((temp_ptr + remainder),
(unsigned char *)buf, size);
fs->dev_desc->block_write(fs->dev_desc->dev,
startblock, 1, sec_buf);
}
} else {
if (size / SECTOR_SIZE != 0) {
fs->dev_desc->block_write(fs->dev_desc->dev,
startblock,
size / SECTOR_SIZE,
(unsigned long *)buf);
} else {
fs->dev_desc->block_read(fs->dev_desc->dev,
startblock, 1, sec_buf);
temp_ptr = sec_buf;
memcpy(temp_ptr, buf, size);
fs->dev_desc->block_write(fs->dev_desc->dev,
startblock, 1,
(unsigned long *)sec_buf);
}
}
}
static int _get_new_inode_no(unsigned char *buffer)
{
struct ext_filesystem *fs = get_fs();
unsigned char input;
int operand, status;
int count = 1;
int j = 0;
/* get the blocksize of the filesystem */
unsigned char *ptr = buffer;
while (*ptr == 255) {
ptr++;
count += 8;
if (count > ext4fs_root->sblock.inodes_per_group)
return -1;
}
for (j = 0; j < fs->blksz; j++) {
input = *ptr;
int i = 0;
while (i <= 7) {
operand = 1 << i;
status = input & operand;
if (status) {
i++;
count++;
} else {
*ptr |= operand;
return count;
}
}
ptr = ptr + 1;
}
return -1;
}
static int _get_new_blk_no(unsigned char *buffer)
{
unsigned char input;
int operand, status;
int count = 0;
int j = 0;
unsigned char *ptr = buffer;
struct ext_filesystem *fs = get_fs();
if (fs->blksz != 1024)
count = 0;
else
count = 1;
while (*ptr == 255) {
ptr++;
count += 8;
if (count == (fs->blksz * 8))
return -1;
}
for (j = 0; j < fs->blksz; j++) {
input = *ptr;
int i = 0;
while (i <= 7) {
operand = 1 << i;
status = input & operand;
if (status) {
i++;
count++;
} else {
*ptr |= operand;
return count;
}
}
ptr = ptr + 1;
}
return -1;
}
int ext4fs_set_block_bmap(long int blockno, unsigned char *buffer, int index)
{
int i, remainder, status;
unsigned char *ptr = buffer;
unsigned char operand;
i = blockno / 8;
remainder = blockno % 8;
int blocksize = EXT2_BLOCK_SIZE(ext4fs_root);
i = i - (index * blocksize);
if (blocksize != 1024) {
ptr = ptr + i;
operand = 1 << remainder;
status = *ptr & operand;
if (status)
return -1;
*ptr = *ptr | operand;
return 0;
} else {
if (remainder == 0) {
ptr = ptr + i - 1;
operand = (1 << 7);
} else {
ptr = ptr + i;
operand = (1 << (remainder - 1));
}
status = *ptr & operand;
if (status)
return -1;
*ptr = *ptr | operand;
return 0;
}
}
void ext4fs_reset_block_bmap(long int blockno, unsigned char *buffer, int index)
{
int i, remainder, status;
unsigned char *ptr = buffer;
unsigned char operand;
i = blockno / 8;
remainder = blockno % 8;
int blocksize = EXT2_BLOCK_SIZE(ext4fs_root);
i = i - (index * blocksize);
if (blocksize != 1024) {
ptr = ptr + i;
operand = (1 << remainder);
status = *ptr & operand;
if (status)
*ptr = *ptr & ~(operand);
} else {
if (remainder == 0) {
ptr = ptr + i - 1;
operand = (1 << 7);
} else {
ptr = ptr + i;
operand = (1 << (remainder - 1));
}
status = *ptr & operand;
if (status)
*ptr = *ptr & ~(operand);
}
}
int ext4fs_set_inode_bmap(int inode_no, unsigned char *buffer, int index)
{
int i, remainder, status;
unsigned char *ptr = buffer;
unsigned char operand;
inode_no -= (index * ext4fs_root->sblock.inodes_per_group);
i = inode_no / 8;
remainder = inode_no % 8;
if (remainder == 0) {
ptr = ptr + i - 1;
operand = (1 << 7);
} else {
ptr = ptr + i;
operand = (1 << (remainder - 1));
}
status = *ptr & operand;
if (status)
return -1;
*ptr = *ptr | operand;
return 0;
}
void ext4fs_reset_inode_bmap(int inode_no, unsigned char *buffer, int index)
{
int i, remainder, status;
unsigned char *ptr = buffer;
unsigned char operand;
inode_no -= (index * ext4fs_root->sblock.inodes_per_group);
i = inode_no / 8;
remainder = inode_no % 8;
if (remainder == 0) {
ptr = ptr + i - 1;
operand = (1 << 7);
} else {
ptr = ptr + i;
operand = (1 << (remainder - 1));
}
status = *ptr & operand;
if (status)
*ptr = *ptr & ~(operand);
}
int ext4fs_checksum_update(unsigned int i)
{
struct ext2_block_group *desc;
struct ext_filesystem *fs = get_fs();
__u16 crc = 0;
desc = (struct ext2_block_group *)&fs->gd[i];
if (fs->sb->feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
int offset = offsetof(struct ext2_block_group, bg_checksum);
crc = ext2fs_crc16(~0, fs->sb->unique_id,
sizeof(fs->sb->unique_id));
crc = ext2fs_crc16(crc, &i, sizeof(i));
crc = ext2fs_crc16(crc, desc, offset);
offset += sizeof(desc->bg_checksum); /* skip checksum */
assert(offset == sizeof(*desc));
}
return crc;
}
static int check_void_in_dentry(struct ext2_dirent *dir, char *filename)
{
int dentry_length;
int sizeof_void_space;
int new_entry_byte_reqd;
short padding_factor = 0;
if (dir->namelen % 4 != 0)
padding_factor = 4 - (dir->namelen % 4);
dentry_length = sizeof(struct ext2_dirent) +
dir->namelen + padding_factor;
sizeof_void_space = dir->direntlen - dentry_length;
if (sizeof_void_space == 0)
return 0;
padding_factor = 0;
if (strlen(filename) % 4 != 0)
padding_factor = 4 - (strlen(filename) % 4);
new_entry_byte_reqd = strlen(filename) +
sizeof(struct ext2_dirent) + padding_factor;
if (sizeof_void_space >= new_entry_byte_reqd) {
dir->direntlen = dentry_length;
return sizeof_void_space;
}
return 0;
}
void ext4fs_update_parent_dentry(char *filename, int *p_ino, int file_type)
{
unsigned int *zero_buffer = NULL;
char *root_first_block_buffer = NULL;
int direct_blk_idx;
long int root_blknr;
long int first_block_no_of_root = 0;
long int previous_blknr = -1;
int totalbytes = 0;
short int padding_factor