2005-04-17 02:20:36 +04:00
/*
2006-03-24 14:15:23 +03:00
* File . . . . . . . . . . . : linux / fs / partitions / ibm . c
2005-04-17 02:20:36 +04:00
* Author ( s ) . . . . . . : Holger Smolinski < Holger . Smolinski @ de . ibm . com >
* Volker Sameske < sameske @ de . ibm . com >
* Bugreports . to . . : < Linux390 @ de . ibm . com >
* ( C ) IBM Corporation , IBM Deutschland Entwicklung GmbH , 1999 , 2000
*/
# include <linux/buffer_head.h>
# include <linux/hdreg.h>
# include <linux/slab.h>
# include <asm/dasd.h>
# include <asm/ebcdic.h>
# include <asm/uaccess.h>
# include <asm/vtoc.h>
# include "check.h"
# include "ibm.h"
/*
2006-03-24 14:15:23 +03:00
* compute the block number from a
2005-04-17 02:20:36 +04:00
* cyl - cyl - head - head structure
*/
2009-03-26 17:23:47 +03:00
static sector_t
2005-11-07 11:59:09 +03:00
cchh2blk ( struct vtoc_cchh * ptr , struct hd_geometry * geo ) {
2009-03-26 17:23:47 +03:00
sector_t cyl ;
__u16 head ;
/*decode cylinder and heads for large volumes */
cyl = ptr - > hh & 0xFFF0 ;
cyl < < = 12 ;
cyl | = ptr - > cc ;
head = ptr - > hh & 0x000F ;
return cyl * geo - > heads * geo - > sectors +
head * geo - > sectors ;
2005-04-17 02:20:36 +04:00
}
/*
2006-03-24 14:15:23 +03:00
* compute the block number from a
2005-04-17 02:20:36 +04:00
* cyl - cyl - head - head - block structure
*/
2009-03-26 17:23:47 +03:00
static sector_t
2005-11-07 11:59:09 +03:00
cchhb2blk ( struct vtoc_cchhb * ptr , struct hd_geometry * geo ) {
2009-03-26 17:23:47 +03:00
sector_t cyl ;
__u16 head ;
/*decode cylinder and heads for large volumes */
cyl = ptr - > hh & 0xFFF0 ;
cyl < < = 12 ;
cyl | = ptr - > cc ;
head = ptr - > hh & 0x000F ;
return cyl * geo - > heads * geo - > sectors +
head * geo - > sectors +
2005-04-17 02:20:36 +04:00
ptr - > b ;
}
/*
*/
2010-05-15 22:09:30 +04:00
int ibm_partition ( struct parsed_partitions * state )
2005-04-17 02:20:36 +04:00
{
2010-05-15 22:09:30 +04:00
struct block_device * bdev = state - > bdev ;
2009-03-26 17:23:47 +03:00
int blocksize , res ;
loff_t i_size , offset , size , fmt_size ;
2007-07-10 13:24:11 +04:00
dasd_information2_t * info ;
2005-04-17 02:20:36 +04:00
struct hd_geometry * geo ;
char type [ 5 ] = { 0 , } ;
char name [ 7 ] = { 0 , } ;
2006-01-06 11:19:09 +03:00
union label_t {
2009-03-26 17:23:47 +03:00
struct vtoc_volume_label_cdl vol ;
struct vtoc_volume_label_ldl lnx ;
2006-01-06 11:19:09 +03:00
struct vtoc_cms_label cms ;
} * label ;
2005-04-17 02:20:36 +04:00
unsigned char * data ;
Sector sect ;
2010-07-19 11:22:35 +04:00
sector_t labelsect ;
2010-08-11 05:03:14 +04:00
char tmp [ 64 ] ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:35:16 +03:00
res = 0 ;
2009-05-23 01:17:49 +04:00
blocksize = bdev_logical_block_size ( bdev ) ;
2006-03-08 08:55:39 +03:00
if ( blocksize < = 0 )
2006-12-07 07:35:16 +03:00
goto out_exit ;
2006-03-08 08:55:39 +03:00
i_size = i_size_read ( bdev - > bd_inode ) ;
if ( i_size = = 0 )
2006-12-07 07:35:16 +03:00
goto out_exit ;
2006-03-08 08:55:39 +03:00
2007-07-10 13:24:11 +04:00
info = kmalloc ( sizeof ( dasd_information2_t ) , GFP_KERNEL ) ;
if ( info = = NULL )
2006-12-07 07:35:16 +03:00
goto out_exit ;
2007-07-10 13:24:11 +04:00
geo = kmalloc ( sizeof ( struct hd_geometry ) , GFP_KERNEL ) ;
if ( geo = = NULL )
2005-04-17 02:20:36 +04:00
goto out_nogeo ;
2007-07-10 13:24:11 +04:00
label = kmalloc ( sizeof ( union label_t ) , GFP_KERNEL ) ;
if ( label = = NULL )
2006-01-06 11:19:09 +03:00
goto out_nolab ;
2006-03-24 14:15:23 +03:00
2007-07-10 13:24:11 +04:00
if ( ioctl_by_bdev ( bdev , BIODASDINFO2 , ( unsigned long ) info ) ! = 0 | |
2005-04-17 02:20:36 +04:00
ioctl_by_bdev ( bdev , HDIO_GETGEO , ( unsigned long ) geo ) ! = 0 )
2006-12-07 07:35:16 +03:00
goto out_freeall ;
2005-04-17 02:20:36 +04:00
2010-07-19 11:22:35 +04:00
/*
* Special case for FBA disks : label sector does not depend on
* blocksize .
*/
if ( ( info - > cu_type = = 0x6310 & & info - > dev_type = = 0x9336 ) | |
( info - > cu_type = = 0x3880 & & info - > dev_type = = 0x3370 ) )
labelsect = info - > label_block ;
else
labelsect = info - > label_block * ( blocksize > > 9 ) ;
2005-04-17 02:20:36 +04:00
/*
* Get volume label , extract name and type .
*/
2010-07-19 11:22:35 +04:00
data = read_part_sector ( state , labelsect , & sect ) ;
2005-04-17 02:20:36 +04:00
if ( data = = NULL )
goto out_readerr ;
2006-01-06 11:19:09 +03:00
memcpy ( label , data , sizeof ( union label_t ) ) ;
2005-04-17 02:20:36 +04:00
put_dev_sector ( sect ) ;
2009-03-26 17:23:47 +03:00
if ( ( ! info - > FBA_layout ) & & ( ! strcmp ( info - > type , " ECKD " ) ) ) {
strncpy ( type , label - > vol . vollbl , 4 ) ;
strncpy ( name , label - > vol . volid , 6 ) ;
} else {
strncpy ( type , label - > lnx . vollbl , 4 ) ;
strncpy ( name , label - > lnx . volid , 6 ) ;
}
2005-04-17 02:20:36 +04:00
EBCASC ( type , 4 ) ;
EBCASC ( name , 6 ) ;
2006-12-07 07:35:16 +03:00
res = 1 ;
2005-04-17 02:20:36 +04:00
/*
2007-07-10 13:24:11 +04:00
* Three different formats : LDL , CDL and unformated disk
*
* identified by info - > format
*
* unformated disks we do not have to care about
2005-04-17 02:20:36 +04:00
*/
2007-07-10 13:24:11 +04:00
if ( info - > format = = DASD_FORMAT_LDL ) {
if ( strncmp ( type , " CMS1 " , 4 ) = = 0 ) {
/*
* VM style CMS1 labeled disk
*/
2009-03-26 17:23:47 +03:00
blocksize = label - > cms . block_size ;
2007-07-10 13:24:11 +04:00
if ( label - > cms . disk_offset ! = 0 ) {
2010-08-11 05:03:14 +04:00
snprintf ( tmp , sizeof ( tmp ) , " CMS1/%8s(MDSK): " , name ) ;
strlcat ( state - > pp_buf , tmp , PAGE_SIZE ) ;
2007-07-10 13:24:11 +04:00
/* disk is reserved minidisk */
offset = label - > cms . disk_offset ;
size = ( label - > cms . block_count - 1 )
* ( blocksize > > 9 ) ;
} else {
2010-08-11 05:03:14 +04:00
snprintf ( tmp , sizeof ( tmp ) , " CMS1/%8s: " , name ) ;
strlcat ( state - > pp_buf , tmp , PAGE_SIZE ) ;
2007-07-10 13:24:11 +04:00
offset = ( info - > label_block + 1 ) ;
2009-03-26 17:23:47 +03:00
size = label - > cms . block_count
* ( blocksize > > 9 ) ;
2007-07-10 13:24:11 +04:00
}
2009-03-26 17:23:47 +03:00
put_partition ( state , 1 , offset * ( blocksize > > 9 ) ,
size - offset * ( blocksize > > 9 ) ) ;
2005-04-17 02:20:36 +04:00
} else {
2009-03-26 17:23:47 +03:00
if ( strncmp ( type , " LNX1 " , 4 ) = = 0 ) {
2010-08-11 05:03:14 +04:00
snprintf ( tmp , sizeof ( tmp ) , " LNX1/%8s: " , name ) ;
strlcat ( state - > pp_buf , tmp , PAGE_SIZE ) ;
2009-03-26 17:23:47 +03:00
if ( label - > lnx . ldl_version = = 0xf2 ) {
fmt_size = label - > lnx . formatted_blocks
* ( blocksize > > 9 ) ;
} else if ( ! strcmp ( info - > type , " ECKD " ) ) {
/* formated w/o large volume support */
fmt_size = geo - > cylinders * geo - > heads
* geo - > sectors * ( blocksize > > 9 ) ;
} else {
/* old label and no usable disk geometry
* ( e . g . DIAG ) */
fmt_size = i_size > > 9 ;
}
size = i_size > > 9 ;
if ( fmt_size < size )
size = fmt_size ;
offset = ( info - > label_block + 1 ) ;
} else {
/* unlabeled disk */
2010-08-13 12:06:43 +04:00
strlcat ( state - > pp_buf , " (nonl) " , PAGE_SIZE ) ;
2009-03-26 17:23:47 +03:00
size = i_size > > 9 ;
offset = ( info - > label_block + 1 ) ;
}
put_partition ( state , 1 , offset * ( blocksize > > 9 ) ,
2007-07-10 13:24:11 +04:00
size - offset * ( blocksize > > 9 ) ) ;
2009-03-26 17:23:47 +03:00
}
2007-07-10 13:24:11 +04:00
} else if ( info - > format = = DASD_FORMAT_CDL ) {
2005-04-17 02:20:36 +04:00
/*
2007-07-10 13:24:11 +04:00
* New style CDL formatted disk
2005-04-17 02:20:36 +04:00
*/
2009-03-26 17:23:47 +03:00
sector_t blk ;
2005-04-17 02:20:36 +04:00
int counter ;
/*
2007-07-10 13:24:11 +04:00
* check if VOL1 label is available
* if not , something is wrong , skipping partition detection
2005-04-17 02:20:36 +04:00
*/
2007-07-10 13:24:11 +04:00
if ( strncmp ( type , " VOL1 " , 4 ) = = 0 ) {
2010-08-11 05:03:14 +04:00
snprintf ( tmp , sizeof ( tmp ) , " VOL1/%8s: " , name ) ;
strlcat ( state - > pp_buf , tmp , PAGE_SIZE ) ;
2007-07-10 13:24:11 +04:00
/*
* get block number and read then go through format1
* labels
*/
blk = cchhb2blk ( & label - > vol . vtoc , geo ) + 1 ;
counter = 0 ;
2010-05-15 22:09:30 +04:00
data = read_part_sector ( state , blk * ( blocksize / 512 ) ,
& sect ) ;
2007-07-10 13:24:11 +04:00
while ( data ! = NULL ) {
struct vtoc_format1_label f1 ;
memcpy ( & f1 , data ,
sizeof ( struct vtoc_format1_label ) ) ;
put_dev_sector ( sect ) ;
/* skip FMT4 / FMT5 / FMT7 labels */
if ( f1 . DS1FMTID = = _ascebc [ ' 4 ' ]
| | f1 . DS1FMTID = = _ascebc [ ' 5 ' ]
2009-03-26 17:23:47 +03:00
| | f1 . DS1FMTID = = _ascebc [ ' 7 ' ]
| | f1 . DS1FMTID = = _ascebc [ ' 9 ' ] ) {
2007-07-10 13:24:11 +04:00
blk + + ;
2010-05-15 22:09:30 +04:00
data = read_part_sector ( state ,
blk * ( blocksize / 512 ) , & sect ) ;
2007-07-10 13:24:11 +04:00
continue ;
}
2009-03-26 17:23:47 +03:00
/* only FMT1 and 8 labels valid at this point */
if ( f1 . DS1FMTID ! = _ascebc [ ' 1 ' ] & &
f1 . DS1FMTID ! = _ascebc [ ' 8 ' ] )
2007-07-10 13:24:11 +04:00
break ;
/* OK, we got valid partition data */
offset = cchh2blk ( & f1 . DS1EXT1 . llimit , geo ) ;
size = cchh2blk ( & f1 . DS1EXT1 . ulimit , geo ) -
offset + geo - > sectors ;
if ( counter > = state - > limit )
break ;
put_partition ( state , counter + 1 ,
offset * ( blocksize > > 9 ) ,
size * ( blocksize > > 9 ) ) ;
counter + + ;
blk + + ;
2010-05-15 22:09:30 +04:00
data = read_part_sector ( state ,
blk * ( blocksize / 512 ) , & sect ) ;
2007-07-10 13:24:11 +04:00
}
if ( ! data )
/* Are we not supposed to report this ? */
goto out_readerr ;
} else
printk ( KERN_WARNING " Warning, expected Label VOL1 not "
" found, treating as CDL formated Disk " ) ;
2005-04-17 02:20:36 +04:00
}
2010-08-11 05:03:14 +04:00
strlcat ( state - > pp_buf , " \n " , PAGE_SIZE ) ;
2006-12-07 07:35:16 +03:00
goto out_freeall ;
2006-03-24 14:15:23 +03:00
2005-04-17 02:20:36 +04:00
out_readerr :
2006-12-07 07:35:16 +03:00
res = - 1 ;
out_freeall :
2006-01-06 11:19:09 +03:00
kfree ( label ) ;
out_nolab :
2005-04-17 02:20:36 +04:00
kfree ( geo ) ;
out_nogeo :
kfree ( info ) ;
2006-12-07 07:35:16 +03:00
out_exit :
return res ;
2005-04-17 02:20:36 +04:00
}