1/* 2 * File...........: linux/fs/partitions/ibm.c 3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 4 * Volker Sameske <sameske@de.ibm.com> 5 * Bugreports.to..: <Linux390@de.ibm.com> 6 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 7 8 * History of changes (starts July 2000) 9 * 07/10/00 Fixed detection of CMS formatted disks 10 * 02/13/00 VTOC partition support added 11 * 12/27/01 fixed PL030593 (CMS reserved minidisk not detected on 64 bit) 12 */ 13 14#include <linux/config.h> 15#include <linux/fs.h> 16#include <linux/genhd.h> 17#include <linux/kernel.h> 18#include <linux/major.h> 19#include <linux/string.h> 20#include <linux/blk.h> 21#include <linux/slab.h> 22#include <linux/hdreg.h> 23#include <linux/ioctl.h> 24#include <linux/version.h> 25#include <asm/ebcdic.h> 26#include <asm/uaccess.h> 27#include <asm/dasd.h> 28 29#include "ibm.h" 30#include "check.h" 31#include <asm/vtoc.h> 32 33/* 34 * compute the block number from a 35 * cyl-cyl-head-head structure 36 */ 37static inline int 38cchh2blk (cchh_t *ptr, struct hd_geometry *geo) { 39 return ptr->cc * geo->heads * geo->sectors + 40 ptr->hh * geo->sectors; 41} 42 43 44/* 45 * compute the block number from a 46 * cyl-cyl-head-head-block structure 47 */ 48static inline int 49cchhb2blk (cchhb_t *ptr, struct hd_geometry *geo) { 50 return ptr->cc * geo->heads * geo->sectors + 51 ptr->hh * geo->sectors + 52 ptr->b; 53} 54 55/* 56 * We used to use ioctl_by_bdev in early 2.4, but it broke 57 * between 2.4.9 and 2.4.18 somewhere. 58 */ 59extern int (*genhd_dasd_ioctl)(struct inode *inp, struct file *filp, 60 unsigned int no, unsigned long data); 61 62static int 63ibm_ioctl_unopened(struct block_device *bdev, unsigned cmd, unsigned long arg) 64{ 65 int res; 66 mm_segment_t old_fs = get_fs(); 67 68 if (genhd_dasd_ioctl == NULL) 69 return -ENODEV; 70 set_fs(KERNEL_DS); 71 res = (*genhd_dasd_ioctl)(bdev->bd_inode, NULL, cmd, arg); 72 set_fs(old_fs); 73 return res; 74} 75 76/* 77 */ 78int 79ibm_partition(struct gendisk *hd, struct block_device *bdev, 80 unsigned long first_sector, int first_part_minor) 81{ 82 int blocksize, offset, size; 83 dasd_information_t *info; 84 struct hd_geometry *geo; 85 char type[5] = {0,}; 86 char name[7] = {0,}; 87 volume_label_t *vlabel; 88 unsigned char *data; 89 Sector sect; 90 91 if ( first_sector != 0 ) 92 BUG(); 93 94 if ((info = kmalloc(sizeof(dasd_information_t), GFP_KERNEL)) == NULL) 95 goto out_noinfo; 96 if ((geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL)) == NULL) 97 goto out_nogeo; 98 if ((vlabel = kmalloc(sizeof(volume_label_t), GFP_KERNEL)) == NULL) 99 goto out_novlab; 100 101 if (ibm_ioctl_unopened(bdev, BIODASDINFO, (unsigned long)info) != 0 || 102 ibm_ioctl_unopened(bdev, HDIO_GETGEO, (unsigned long)geo) != 0) 103 goto out_noioctl; 104 105 if ((blocksize = get_hardsect_size(to_kdev_t(bdev->bd_dev))) <= 0) 106 goto out_badsect; 107 108 /* 109 * Get volume label, extract name and type. 110 */ 111 data = read_dev_sector(bdev, info->label_block*(blocksize/512), §); 112 if (data == NULL) 113 goto out_readerr; 114 strncpy (type, data, 4); 115 if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) 116 strncpy(name, data + 8, 6); 117 else 118 strncpy(name, data + 4, 6); 119 memcpy (vlabel, data, sizeof(volume_label_t)); 120 put_dev_sector(sect); 121 122 EBCASC(type, 4); 123 EBCASC(name, 6); 124 125 /* 126 * Three different types: CMS1, VOL1 and LNX1/unlabeled 127 */ 128 if (strncmp(type, "CMS1", 4) == 0) { 129 /* 130 * VM style CMS1 labeled disk 131 */ 132 int *label = (int *) data; 133 134 if (label[13] != 0) { 135 printk("CMS1/%8s(MDSK):", name); 136 /* disk is reserved minidisk */ 137 blocksize = label[3]; 138 offset = label[13]; 139 size = (label[7] - 1)*(blocksize >> 9); 140 } else { 141 printk("CMS1/%8s:", name); 142 offset = (info->label_block + 1); 143 size = bdev->bd_inode->i_size >> 9; 144 } 145 // add_gd_partition(hd, first_part_minor - 1, 0, size); 146 add_gd_partition(hd, first_part_minor, 147 offset*(blocksize >> 9), 148 size-offset*(blocksize >> 9)); 149 } else if (strncmp(type, "VOL1", 4) == 0) { 150 /* 151 * New style VOL1 labeled disk 152 */ 153 unsigned int blk; 154 int counter; 155 156 printk("VOL1/%8s:", name); 157 158 /* get block number and read then go through format1 labels */ 159 blk = cchhb2blk(&vlabel->vtoc, geo) + 1; 160 counter = 0; 161 while ((data = read_dev_sector(bdev, blk*(blocksize/512), 162 §)) != NULL) { 163 format1_label_t f1; 164 165 memcpy(&f1, data, sizeof(format1_label_t)); 166 put_dev_sector(sect); 167 168 /* skip FMT4 / FMT5 / FMT7 labels */ 169 if (f1.DS1FMTID == _ascebc['4'] 170 || f1.DS1FMTID == _ascebc['5'] 171 || f1.DS1FMTID == _ascebc['7']) { 172 blk++; 173 continue; 174 } 175 176 /* only FMT1 valid at this point */ 177 if (f1.DS1FMTID != _ascebc['1']) 178 break; 179 180 /* OK, we got valid partition data */ 181 offset = cchh2blk(&f1.DS1EXT1.llimit, geo); 182 size = cchh2blk(&f1.DS1EXT1.ulimit, geo) - 183 offset + geo->sectors; 184 if (counter >= hd->max_p) 185 break; 186 add_gd_partition(hd, first_part_minor + counter, 187 offset * (blocksize >> 9), 188 size * (blocksize >> 9)); 189 counter++; 190 blk++; 191 } 192 } else { 193 /* 194 * Old style LNX1 or unlabeled disk 195 */ 196 if (strncmp(type, "LNX1", 4) == 0) 197 printk ("LNX1/%8s:", name); 198 else 199 printk("(nonl)/%8s:", name); 200 offset = (info->label_block + 1); 201 size = (bdev->bd_inode->i_size >> 9); 202 // add_gd_partition(hd, first_part_minor - 1, 0, size); 203 add_gd_partition(hd, first_part_minor, 204 offset*(blocksize >> 9), 205 size-offset*(blocksize >> 9)); 206 } 207 208 printk("\n"); 209 kfree(vlabel); 210 kfree(geo); 211 kfree(info); 212 return 1; 213 214out_readerr: 215out_badsect: 216out_noioctl: 217 kfree(vlabel); 218out_novlab: 219 kfree(geo); 220out_nogeo: 221 kfree(info); 222out_noinfo: 223 return 0; 224} 225