blob: 12bbf2b03fe85aa25183d9712722087cb0ba8d78 [file] [log] [blame]
/*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* fdisk command for U-boot
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <common.h>
#include <command.h>
#include <mmc.h>
#define BLOCK_SIZE 512
#define BLOCK_END 0xFFFFFFFF
#define _10MB (10*1024*1024)
#define _100MB (100*1024*1024)
#define _8_4GB (1023*254*63)
#define SYSTEM_PART_SIZE (400*1024*1024)
#define USER_DATA_PART_SIZE (2917*1024*1024UL)
#define CACHE_PART_SIZE (200*1024*1024)
#define CHS_MODE 0
#define LBA_MODE !(CHS_MODE)
typedef struct
{
int C_start;
int H_start;
int S_start;
int C_end;
int H_end;
int S_end;
int available_block;
int unit;
int total_block_count;
int addr_mode; // LBA_MODE or CHS_MODE
} SDInfo;
typedef struct
{
unsigned char bootable;
unsigned char partitionId;
int C_start;
int H_start;
int S_start;
int C_end;
int H_end;
int S_end;
unsigned long block_start;
unsigned long block_count;
int block_end;
} PartitionInfo;
/////////////////////////////////////////////////////////////////
int calc_unit(unsigned long long length, SDInfo sdInfo)
{
if (sdInfo.addr_mode == CHS_MODE)
return ( (length / BLOCK_SIZE / sdInfo.unit + 1 ) * sdInfo.unit);
else
return ( (length / BLOCK_SIZE) );
}
/////////////////////////////////////////////////////////////////
void encode_chs(int C, int H, int S, unsigned char *result)
{
*result++ = (unsigned char) H;
*result++ = (unsigned char) ( S + ((C & 0x00000300) >> 2) );
*result = (unsigned char) (C & 0x000000FF);
}
/////////////////////////////////////////////////////////////////
void encode_partitionInfo(PartitionInfo partInfo, unsigned char *result)
{
*result++ = partInfo.bootable;
encode_chs(partInfo.C_start, partInfo.H_start, partInfo.S_start, result);
result +=3;
*result++ = partInfo.partitionId;
encode_chs(partInfo.C_end, partInfo.H_end, partInfo.S_end, result);
result += 3;
memcpy(result, (unsigned char *)&(partInfo.block_start), 4);
result += 4;
memcpy(result, (unsigned char *)&(partInfo.block_count), 4);
}
/////////////////////////////////////////////////////////////////
void decode_partitionInfo(unsigned char *in, PartitionInfo *partInfo)
{
partInfo->bootable = *in;
partInfo->partitionId = *(in + 4);
memcpy((unsigned char *)&(partInfo->block_start), (in + 8), 4);
memcpy((unsigned char *)&(partInfo->block_count), (in +12), 4);
}
/////////////////////////////////////////////////////////////////
void get_SDInfo(int block_count, SDInfo *sdInfo)
{
int C, H, S;
int C_max = 1023, H_max = 255, S_max = 63;
int H_start = 1, S_start = 1;
int diff_min = 0, diff = 0;
if(block_count >= _8_4GB)
sdInfo->addr_mode = LBA_MODE;
else
sdInfo->addr_mode = CHS_MODE;
//-----------------------------------------------------
if (sdInfo->addr_mode == CHS_MODE)
{
diff_min = C_max;
for (H = H_start; H <= H_max; H++)
for (S = S_start; S <= S_max; S++)
{
C = block_count / (H * S);
if ( (C <= C_max) )
{
diff = C_max - C;
if (diff <= diff_min)
{
diff_min = diff;
sdInfo->C_end = C;
sdInfo->H_end = H;
sdInfo->S_end = S;
}
}
}
}
//-----------------------------------------------------
else
{
sdInfo->C_end = 1023;
sdInfo->H_end = 254;
sdInfo->S_end = 63;
}
//-----------------------------------------------------
sdInfo->C_start = 0;
sdInfo->H_start = 1;
sdInfo->S_start = 1;
sdInfo->total_block_count = block_count;
sdInfo->available_block = sdInfo->C_end * sdInfo->H_end * sdInfo->S_end;
sdInfo->unit = sdInfo->H_end * sdInfo->S_end;
}
/////////////////////////////////////////////////////////////////
void make_partitionInfo(int LBA_start, int count, SDInfo sdInfo, PartitionInfo *partInfo)
{
int temp = 0;
int _10MB_unit;
partInfo->block_start = LBA_start;
//-----------------------------------------------------
if (sdInfo.addr_mode == CHS_MODE)
{
partInfo->C_start = partInfo->block_start / (sdInfo.H_end * sdInfo.S_end);
temp = partInfo->block_start % (sdInfo.H_end * sdInfo.S_end);
partInfo->H_start = temp / sdInfo.S_end;
partInfo->S_start = temp % sdInfo.S_end + 1;
if (count == BLOCK_END)
{
_10MB_unit = calc_unit(CFG_PARTITION_START, sdInfo);
partInfo->block_end = sdInfo.C_end * sdInfo.H_end * sdInfo.S_end - _10MB_unit - 1;
partInfo->block_count = partInfo->block_end - partInfo->block_start + 1;
partInfo->C_end = partInfo->block_end / sdInfo.unit;
partInfo->H_end = sdInfo.H_end - 1;
partInfo->S_end = sdInfo.S_end;
}
else
{
partInfo->block_count = count;
partInfo->block_end = partInfo->block_start + count - 1;
partInfo->C_end = partInfo->block_end / sdInfo.unit;
temp = partInfo->block_end % sdInfo.unit;
partInfo->H_end = temp / sdInfo.S_end;
partInfo->S_end = temp % sdInfo.S_end + 1;
}
}
//-----------------------------------------------------
else
{
partInfo->C_start = 0;
partInfo->H_start = 1;
partInfo->S_start = 1;
partInfo->C_end = 1023;
partInfo->H_end = 254;
partInfo->S_end = 63;
if (count == BLOCK_END)
{
_10MB_unit = calc_unit(CFG_PARTITION_START, sdInfo);
partInfo->block_end = sdInfo.total_block_count - _10MB_unit - 1;
partInfo->block_count = partInfo->block_end - partInfo->block_start + 1;
}
else
{
partInfo->block_count = count;
partInfo->block_end = partInfo->block_start + count - 1;
}
}
}
/////////////////////////////////////////////////////////////////
int make_mmc_partition(int total_block_count, unsigned char *mbr, int flag, char *argv[])
{
int block_start = 0, block_offset;
SDInfo sdInfo;
PartitionInfo partInfo[4];
///////////////////////////////////////////////////////////
memset((unsigned char *)&sdInfo, 0x00, sizeof(SDInfo));
///////////////////////////////////////////////////////////
get_SDInfo(total_block_count, &sdInfo);
///////////////////////////////////////////////////////////
// ¹Ýµå½Ã Unit´ÜÀ§·Î ¸ÕÀú °è»êÇÑ´Ù.
block_start = calc_unit(CFG_PARTITION_START, sdInfo);
if (flag)
block_offset = calc_unit((unsigned long long)simple_strtoul(argv[3], NULL, 0)*1024*1024, sdInfo);
else
block_offset = calc_unit(SYSTEM_PART_SIZE, sdInfo);
partInfo[0].bootable = 0x00;
partInfo[0].partitionId = 0x83;
make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[0]);
///////////////////////////////////////////////////////////
block_start += block_offset;
if (flag)
block_offset = calc_unit((unsigned long long)simple_strtoul(argv[4], NULL, 0)*1024*1024, sdInfo);
else
block_offset = calc_unit(USER_DATA_PART_SIZE, sdInfo);
partInfo[1].bootable = 0x00;
partInfo[1].partitionId = 0x83;
make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[1]);
///////////////////////////////////////////////////////////
block_start += block_offset;
if (flag)
block_offset = calc_unit((unsigned long long)simple_strtoul(argv[5], NULL, 0)*1024*1024, sdInfo);
else
block_offset = calc_unit(CACHE_PART_SIZE, sdInfo);
partInfo[2].bootable = 0x00;
partInfo[2].partitionId = 0x83;
make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[2]);
///////////////////////////////////////////////////////////
block_start += block_offset;
block_offset = BLOCK_END;
partInfo[3].bootable = 0x00;
partInfo[3].partitionId = 0x0C;
make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[3]);
///////////////////////////////////////////////////////////
memset(mbr, 0x00, sizeof(mbr));
mbr[510] = 0x55; mbr[511] = 0xAA;
encode_partitionInfo(partInfo[0], &mbr[0x1CE]);
encode_partitionInfo(partInfo[1], &mbr[0x1DE]);
encode_partitionInfo(partInfo[2], &mbr[0x1EE]);
encode_partitionInfo(partInfo[3], &mbr[0x1BE]);
return 0;
}
void print_mmc_blk(unsigned char* buf)
{
int i;
for (i = 1; i <= 512; i++) {
printf("0x%02x ", buf[i-1]);
if (i % 16 == 0) {
printf("\n");
}
}
}
int dump_mmc(unsigned long start_blk, int end_blk)
{
struct mmc* mmc;
lbaint_t blkcnt;
int rv;
unsigned char buf[512] = {0,};
mmc = find_mmc_device(0);
if (!mmc)
{
printf("mmc/sd device is NOT founded.\n");
return -1;
}
blkcnt = end_blk - start_blk + 1;
while (blkcnt--)
{
rv = mmc->block_dev.block_read(0, start_blk++, 1, buf);
if(rv != 1)
{
printf("mmc/sd device read fail.\n");
return -1;
}
print_mmc_blk(buf);
}
return 0;
}
/////////////////////////////////////////////////////////////////
int get_mmc_block_count(char *device_name)
{
int rv;
struct mmc *mmc;
int block_count = 0;
int dev_num;
dev_num = simple_strtoul(device_name, NULL, 0);
mmc = find_mmc_device(dev_num);
if (!mmc)
{
printf("mmc/sd device is NOT founded.\n");
return -1;
}
block_count = mmc->capacity / mmc->read_bl_len;
// printf("block_count = %d\n", block_count);
return block_count;
}
/////////////////////////////////////////////////////////////////
int get_mmc_mbr(char *device_name, unsigned char *mbr)
{
int rv;
struct mmc *mmc;
int dev_num;
dev_num = simple_strtoul(device_name, NULL, 0);
mmc = find_mmc_device(dev_num);
if (!mmc)
{
printf("mmc/sd device is NOT founded.\n");
return -1;
}
rv = mmc->block_dev.block_read(dev_num, 0, 1, mbr);
if(rv == 1)
return 0;
else
return -1;
}
/////////////////////////////////////////////////////////////////
int put_mmc_mbr(unsigned char *mbr, char *device_name)
{
int rv;
struct mmc *mmc;
int dev_num, err;
dev_num = simple_strtoul(device_name, NULL, 0);
mmc = find_mmc_device(dev_num);
if (!mmc)
{
printf("mmc/sd device is NOT founded.\n");
return -1;
}
rv = mmc->block_dev.block_write(dev_num, 0, 1, mbr);
/*
* TODO : set boot partition size for emmc
* mmc->ext_csd.boot_size_multi = 0;
*/
err = mmc_init(mmc);
if (err) {
printf("Card NOT detected or Init Failed!!\n");
return err;
}
if(rv == 1)
return 0;
else
return -1;
}
int get_boot_part_info(int dev, int part_num, unsigned long *block_start, unsigned long *block_count, unsigned char *part_Id)
{
block_dev_desc_t *dev_desc;
char if_name[32] = "mmc";
dev_desc = get_dev(if_name, dev);
return get_mmc_part_info(dev_desc, dev, part_num, block_start, block_count, part_Id);
}
int get_mmc_part_info(block_dev_desc_t *dev_desc, int dev, int part_num, unsigned long *block_start, unsigned long *block_count, unsigned char *part_Id)
{
struct mmc *mmc;
if (!dev_desc || !block_start || !block_count || !part_Id) {
printf("mmc/sd partition argument invalid.\n");
return -1;
}
if (dev_desc->part_type == PART_TYPE_DOS) {
return get_mmc_mbr_part_info(dev_desc, dev, part_num, block_start, block_count, part_Id);
}
#ifdef CONFIG_EFI_PARTITION
else if (dev_desc->part_type == PART_TYPE_EFI) {
return get_mmc_gpt_part_info(dev_desc, dev, part_num, block_start, block_count, part_Id);
}
#endif
else {
printf("mmc/sd unknown partition type : 0x%x.\n", dev_desc->part_type);
return -1;
}
}
#ifdef CONFIG_EFI_PARTITION
int find_gpt_part_name(char *name)
{
block_dev_desc_t *dev_desc;
char if_name[32] = "mmc";
dev_desc = get_dev(if_name, 0);
return find_part_name(dev_desc, name);
}
int get_mmc_gpt_part_info(block_dev_desc_t *blkdev, int dev, int part_num, unsigned long *block_start, unsigned long *block_count, unsigned char *part_type)
{
disk_partition_t info;
if (!blkdev || !block_start || !block_count || !part_type) {
printf("%s : argument invalid\n", __func__);
}
if (get_partition_info_efi(blkdev, part_num, &info))
{
printf("mmc/sd failed to get partition info.\n");
return -1;
}
*block_start = info.start;
*block_count = info.size;
*part_type = 0xee; /* this is efi part type for mbr */
return 0;
}
#endif
/////////////////////////////////////////////////////////////////
int get_mmc_mbr_part_info(block_dev_desc_t *dev_desc, int dev, int part_num, unsigned long *block_start, unsigned long *block_count, unsigned char *part_Id)
{
int rv;
PartitionInfo partInfo;
#if defined(CONFIG_MMC_64BIT_BUS) || defined(CONFIG_CPU_EXYNOS5410_EVT2)
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, mbr, 512);
#else
unsigned char mbr[512];
#endif
rv = dev_desc->block_read(dev, 0, 1, mbr);
if(!rv)
return -1;
decode_partitionInfo(&mbr[0x1AE + 0x10*part_num], &partInfo);
*block_start = partInfo.block_start * (dev_desc->blksz/512);
*block_count = partInfo.block_count * (dev_desc->blksz/512);
*part_Id = partInfo.partitionId;
return 0;
}
/////////////////////////////////////////////////////////////////
int print_mmc_part_info(int argc, char *argv[])
{
int i;
int dev;
block_dev_desc_t *dev_desc;
char if_name[32] = "mmc";
PartitionInfo partInfo[4];
dev = simple_strtoul(argv[2], NULL, 10);
dev_desc = get_dev(if_name, dev);
if (!dev_desc)
return -1;
for ( i=0; i< 4; i++) {
get_mmc_part_info(dev_desc, dev, i+1, &(partInfo[i].block_start),
&(partInfo[i].block_count), &(partInfo[i].partitionId) );
}
printf("\n");
printf("partion # size(MB) block start # block count partition_Id (block size=%d)\n", dev_desc->blksz);
for ( i=0; i< 4; i++) {
if ( (partInfo[i].block_start !=0) && (partInfo[i].block_count != 0) )
printf(" %d %6d %8d %8d 0x%02X\n",
i+1, (partInfo[i].block_count /2048),
partInfo[i].block_start, partInfo[i].block_count, partInfo[i].partitionId);
}
return 1;
}
/////////////////////////////////////////////////////////////////
int create_mmc_fdisk(int argc, char *argv[])
{
int rv;
int total_block_count;
#if defined(CONFIG_MMC_64BIT_BUS) || defined(CONFIG_CPU_EXYNOS5410_EVT2)
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, mbr, 512);
#else
unsigned char mbr[512];
#endif
struct mmc *mmc;
int dev_num;
dev_num = simple_strtoul(argv[2], NULL, 0);
mmc = find_mmc_device(dev_num);
memset(mbr, 0x00, 512);
total_block_count = get_mmc_block_count(argv[2]);
if (total_block_count < 0)
return -1;
make_mmc_partition(total_block_count, mbr, (argc==6?1:0), argv);
rv = put_mmc_mbr(mbr, argv[2]);
if (rv != 0)
return -1;
init_part(&mmc->block_dev);
printf("fdisk is completed\n");
argv[1][1] = 'p';
print_mmc_part_info(argc, argv);
return 0;
}
/////////////////////////////////////////////////////////////////
int do_fdisk(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
if ( argc == 3 || argc ==6 )
{
if ( strcmp(argv[1], "-c") == 0 )
return create_mmc_fdisk(argc, argv);
else if ( strcmp(argv[1], "-p") == 0 )
return print_mmc_part_info(argc, argv);
}
else
{
printf("Usage:\nfdisk <-p> <device_num>\n");
printf("fdisk <-c> <device_num> [<sys. part size(MB)> <user data part size> <cache part size>]\n");
}
return 0;
}
U_BOOT_CMD(
fdisk, 6, 0, do_fdisk,
"fdisk\t- fdisk for sd/mmc.\n",
"fdisk -p <device_num>\t- print partition information\n"
"fdisk -c <device_num> [<sys. part size(MB)> <user data part size> <cache part size>]\t- create partition.\n"
);