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 9 #include <linux/buffer_head.h> 10 #include <linux/hdreg.h> 11 #include <linux/slab.h> 12 #include <asm/dasd.h> 13 #include <asm/ebcdic.h> 14 #include <asm/uaccess.h> 15 #include <asm/vtoc.h> 16 17 #include "check.h" 18 #include "ibm.h" 19 20 /* 21 * compute the block number from a 22 * cyl-cyl-head-head structure 23 */ 24 static sector_t 25 cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) { 26 27 sector_t cyl; 28 __u16 head; 29 30 /*decode cylinder and heads for large volumes */ 31 cyl = ptr->hh & 0xFFF0; 32 cyl <<= 12; 33 cyl |= ptr->cc; 34 head = ptr->hh & 0x000F; 35 return cyl * geo->heads * geo->sectors + 36 head * geo->sectors; 37 } 38 39 /* 40 * compute the block number from a 41 * cyl-cyl-head-head-block structure 42 */ 43 static sector_t 44 cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) { 45 46 sector_t cyl; 47 __u16 head; 48 49 /*decode cylinder and heads for large volumes */ 50 cyl = ptr->hh & 0xFFF0; 51 cyl <<= 12; 52 cyl |= ptr->cc; 53 head = ptr->hh & 0x000F; 54 return cyl * geo->heads * geo->sectors + 55 head * geo->sectors + 56 ptr->b; 57 } 58 59 /* 60 */ 61 int ibm_partition(struct parsed_partitions *state) 62 { 63 struct block_device *bdev = state->bdev; 64 int blocksize, res; 65 loff_t i_size, offset, size, fmt_size; 66 dasd_information2_t *info; 67 struct hd_geometry *geo; 68 char type[5] = {0,}; 69 char name[7] = {0,}; 70 union label_t { 71 struct vtoc_volume_label_cdl vol; 72 struct vtoc_volume_label_ldl lnx; 73 struct vtoc_cms_label cms; 74 } *label; 75 unsigned char *data; 76 Sector sect; 77 sector_t labelsect; 78 char tmp[64]; 79 80 res = 0; 81 blocksize = bdev_logical_block_size(bdev); 82 if (blocksize <= 0) 83 goto out_exit; 84 i_size = i_size_read(bdev->bd_inode); 85 if (i_size == 0) 86 goto out_exit; 87 88 info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL); 89 if (info == NULL) 90 goto out_exit; 91 geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL); 92 if (geo == NULL) 93 goto out_nogeo; 94 label = kmalloc(sizeof(union label_t), GFP_KERNEL); 95 if (label == NULL) 96 goto out_nolab; 97 98 if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0 || 99 ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0) 100 goto out_freeall; 101 102 /* 103 * Special case for FBA disks: label sector does not depend on 104 * blocksize. 105 */ 106 if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) || 107 (info->cu_type == 0x3880 && info->dev_type == 0x3370)) 108 labelsect = info->label_block; 109 else 110 labelsect = info->label_block * (blocksize >> 9); 111 112 /* 113 * Get volume label, extract name and type. 114 */ 115 data = read_part_sector(state, labelsect, §); 116 if (data == NULL) 117 goto out_readerr; 118 119 memcpy(label, data, sizeof(union label_t)); 120 put_dev_sector(sect); 121 122 if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) { 123 strncpy(type, label->vol.vollbl, 4); 124 strncpy(name, label->vol.volid, 6); 125 } else { 126 strncpy(type, label->lnx.vollbl, 4); 127 strncpy(name, label->lnx.volid, 6); 128 } 129 EBCASC(type, 4); 130 EBCASC(name, 6); 131 132 res = 1; 133 134 /* 135 * Three different formats: LDL, CDL and unformated disk 136 * 137 * identified by info->format 138 * 139 * unformated disks we do not have to care about 140 */ 141 if (info->format == DASD_FORMAT_LDL) { 142 if (strncmp(type, "CMS1", 4) == 0) { 143 /* 144 * VM style CMS1 labeled disk 145 */ 146 blocksize = label->cms.block_size; 147 if (label->cms.disk_offset != 0) { 148 snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name); 149 strlcat(state->pp_buf, tmp, PAGE_SIZE); 150 /* disk is reserved minidisk */ 151 offset = label->cms.disk_offset; 152 size = (label->cms.block_count - 1) 153 * (blocksize >> 9); 154 } else { 155 snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name); 156 strlcat(state->pp_buf, tmp, PAGE_SIZE); 157 offset = (info->label_block + 1); 158 size = label->cms.block_count 159 * (blocksize >> 9); 160 } 161 put_partition(state, 1, offset*(blocksize >> 9), 162 size-offset*(blocksize >> 9)); 163 } else { 164 if (strncmp(type, "LNX1", 4) == 0) { 165 snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name); 166 strlcat(state->pp_buf, tmp, PAGE_SIZE); 167 if (label->lnx.ldl_version == 0xf2) { 168 fmt_size = label->lnx.formatted_blocks 169 * (blocksize >> 9); 170 } else if (!strcmp(info->type, "ECKD")) { 171 /* formated w/o large volume support */ 172 fmt_size = geo->cylinders * geo->heads 173 * geo->sectors * (blocksize >> 9); 174 } else { 175 /* old label and no usable disk geometry 176 * (e.g. DIAG) */ 177 fmt_size = i_size >> 9; 178 } 179 size = i_size >> 9; 180 if (fmt_size < size) 181 size = fmt_size; 182 offset = (info->label_block + 1); 183 } else { 184 /* unlabeled disk */ 185 strlcat(state->pp_buf, "(nonl)", PAGE_SIZE); 186 size = i_size >> 9; 187 offset = (info->label_block + 1); 188 } 189 put_partition(state, 1, offset*(blocksize >> 9), 190 size-offset*(blocksize >> 9)); 191 } 192 } else if (info->format == DASD_FORMAT_CDL) { 193 /* 194 * New style CDL formatted disk 195 */ 196 sector_t blk; 197 int counter; 198 199 /* 200 * check if VOL1 label is available 201 * if not, something is wrong, skipping partition detection 202 */ 203 if (strncmp(type, "VOL1", 4) == 0) { 204 snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name); 205 strlcat(state->pp_buf, tmp, PAGE_SIZE); 206 /* 207 * get block number and read then go through format1 208 * labels 209 */ 210 blk = cchhb2blk(&label->vol.vtoc, geo) + 1; 211 counter = 0; 212 data = read_part_sector(state, blk * (blocksize/512), 213 §); 214 while (data != NULL) { 215 struct vtoc_format1_label f1; 216 217 memcpy(&f1, data, 218 sizeof(struct vtoc_format1_label)); 219 put_dev_sector(sect); 220 221 /* skip FMT4 / FMT5 / FMT7 labels */ 222 if (f1.DS1FMTID == _ascebc['4'] 223 || f1.DS1FMTID == _ascebc['5'] 224 || f1.DS1FMTID == _ascebc['7'] 225 || f1.DS1FMTID == _ascebc['9']) { 226 blk++; 227 data = read_part_sector(state, 228 blk * (blocksize/512), §); 229 continue; 230 } 231 232 /* only FMT1 and 8 labels valid at this point */ 233 if (f1.DS1FMTID != _ascebc['1'] && 234 f1.DS1FMTID != _ascebc['8']) 235 break; 236 237 /* OK, we got valid partition data */ 238 offset = cchh2blk(&f1.DS1EXT1.llimit, geo); 239 size = cchh2blk(&f1.DS1EXT1.ulimit, geo) - 240 offset + geo->sectors; 241 if (counter >= state->limit) 242 break; 243 put_partition(state, counter + 1, 244 offset * (blocksize >> 9), 245 size * (blocksize >> 9)); 246 counter++; 247 blk++; 248 data = read_part_sector(state, 249 blk * (blocksize/512), §); 250 } 251 252 if (!data) 253 /* Are we not supposed to report this ? */ 254 goto out_readerr; 255 } else 256 printk(KERN_INFO "Expected Label VOL1 not " 257 "found, treating as CDL formated Disk"); 258 259 } 260 261 strlcat(state->pp_buf, "\n", PAGE_SIZE); 262 goto out_freeall; 263 264 265 out_readerr: 266 res = -1; 267 out_freeall: 268 kfree(label); 269 out_nolab: 270 kfree(geo); 271 out_nogeo: 272 kfree(info); 273 out_exit: 274 return res; 275 } 276