root/block/partitions/ibm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cchh2blk
  2. cchhb2blk
  3. find_label
  4. find_vol1_partitions
  5. find_lnx1_partitions
  6. find_cms1_partitions
  7. ibm_partition

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   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  * Copyright IBM Corp. 1999, 2012
   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 <linux/uaccess.h>
  15 #include <asm/vtoc.h>
  16 
  17 #include "check.h"
  18 #include "ibm.h"
  19 
  20 
  21 union label_t {
  22         struct vtoc_volume_label_cdl vol;
  23         struct vtoc_volume_label_ldl lnx;
  24         struct vtoc_cms_label cms;
  25 };
  26 
  27 /*
  28  * compute the block number from a
  29  * cyl-cyl-head-head structure
  30  */
  31 static sector_t cchh2blk(struct vtoc_cchh *ptr, struct hd_geometry *geo)
  32 {
  33         sector_t cyl;
  34         __u16 head;
  35 
  36         /* decode cylinder and heads for large volumes */
  37         cyl = ptr->hh & 0xFFF0;
  38         cyl <<= 12;
  39         cyl |= ptr->cc;
  40         head = ptr->hh & 0x000F;
  41         return cyl * geo->heads * geo->sectors +
  42                head * geo->sectors;
  43 }
  44 
  45 /*
  46  * compute the block number from a
  47  * cyl-cyl-head-head-block structure
  48  */
  49 static sector_t cchhb2blk(struct vtoc_cchhb *ptr, struct hd_geometry *geo)
  50 {
  51         sector_t cyl;
  52         __u16 head;
  53 
  54         /* decode cylinder and heads for large volumes */
  55         cyl = ptr->hh & 0xFFF0;
  56         cyl <<= 12;
  57         cyl |= ptr->cc;
  58         head = ptr->hh & 0x000F;
  59         return  cyl * geo->heads * geo->sectors +
  60                 head * geo->sectors +
  61                 ptr->b;
  62 }
  63 
  64 static int find_label(struct parsed_partitions *state,
  65                       dasd_information2_t *info,
  66                       struct hd_geometry *geo,
  67                       int blocksize,
  68                       sector_t *labelsect,
  69                       char name[],
  70                       char type[],
  71                       union label_t *label)
  72 {
  73         Sector sect;
  74         unsigned char *data;
  75         sector_t testsect[3];
  76         unsigned char temp[5];
  77         int found = 0;
  78         int i, testcount;
  79 
  80         /* There a three places where we may find a valid label:
  81          * - on an ECKD disk it's block 2
  82          * - on an FBA disk it's block 1
  83          * - on an CMS formatted FBA disk it is sector 1, even if the block size
  84          *   is larger than 512 bytes (possible if the DIAG discipline is used)
  85          * If we have a valid info structure, then we know exactly which case we
  86          * have, otherwise we just search through all possebilities.
  87          */
  88         if (info) {
  89                 if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
  90                     (info->cu_type == 0x3880 && info->dev_type == 0x3370))
  91                         testsect[0] = info->label_block;
  92                 else
  93                         testsect[0] = info->label_block * (blocksize >> 9);
  94                 testcount = 1;
  95         } else {
  96                 testsect[0] = 1;
  97                 testsect[1] = (blocksize >> 9);
  98                 testsect[2] = 2 * (blocksize >> 9);
  99                 testcount = 3;
 100         }
 101         for (i = 0; i < testcount; ++i) {
 102                 data = read_part_sector(state, testsect[i], &sect);
 103                 if (data == NULL)
 104                         continue;
 105                 memcpy(label, data, sizeof(*label));
 106                 memcpy(temp, data, 4);
 107                 temp[4] = 0;
 108                 EBCASC(temp, 4);
 109                 put_dev_sector(sect);
 110                 if (!strcmp(temp, "VOL1") ||
 111                     !strcmp(temp, "LNX1") ||
 112                     !strcmp(temp, "CMS1")) {
 113                         if (!strcmp(temp, "VOL1")) {
 114                                 strncpy(type, label->vol.vollbl, 4);
 115                                 strncpy(name, label->vol.volid, 6);
 116                         } else {
 117                                 strncpy(type, label->lnx.vollbl, 4);
 118                                 strncpy(name, label->lnx.volid, 6);
 119                         }
 120                         EBCASC(type, 4);
 121                         EBCASC(name, 6);
 122                         *labelsect = testsect[i];
 123                         found = 1;
 124                         break;
 125                 }
 126         }
 127         if (!found)
 128                 memset(label, 0, sizeof(*label));
 129 
 130         return found;
 131 }
 132 
 133 static int find_vol1_partitions(struct parsed_partitions *state,
 134                                 struct hd_geometry *geo,
 135                                 int blocksize,
 136                                 char name[],
 137                                 union label_t *label)
 138 {
 139         sector_t blk;
 140         int counter;
 141         char tmp[64];
 142         Sector sect;
 143         unsigned char *data;
 144         loff_t offset, size;
 145         struct vtoc_format1_label f1;
 146         int secperblk;
 147 
 148         snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
 149         strlcat(state->pp_buf, tmp, PAGE_SIZE);
 150         /*
 151          * get start of VTOC from the disk label and then search for format1
 152          * and format8 labels
 153          */
 154         secperblk = blocksize >> 9;
 155         blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
 156         counter = 0;
 157         data = read_part_sector(state, blk * secperblk, &sect);
 158         while (data != NULL) {
 159                 memcpy(&f1, data, sizeof(struct vtoc_format1_label));
 160                 put_dev_sector(sect);
 161                 /* skip FMT4 / FMT5 / FMT7 labels */
 162                 if (f1.DS1FMTID == _ascebc['4']
 163                     || f1.DS1FMTID == _ascebc['5']
 164                     || f1.DS1FMTID == _ascebc['7']
 165                     || f1.DS1FMTID == _ascebc['9']) {
 166                         blk++;
 167                         data = read_part_sector(state, blk * secperblk, &sect);
 168                         continue;
 169                 }
 170                 /* only FMT1 and 8 labels valid at this point */
 171                 if (f1.DS1FMTID != _ascebc['1'] &&
 172                     f1.DS1FMTID != _ascebc['8'])
 173                         break;
 174                 /* OK, we got valid partition data */
 175                 offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
 176                 size  = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
 177                         offset + geo->sectors;
 178                 offset *= secperblk;
 179                 size *= secperblk;
 180                 if (counter >= state->limit)
 181                         break;
 182                 put_partition(state, counter + 1, offset, size);
 183                 counter++;
 184                 blk++;
 185                 data = read_part_sector(state, blk * secperblk, &sect);
 186         }
 187         strlcat(state->pp_buf, "\n", PAGE_SIZE);
 188 
 189         if (!data)
 190                 return -1;
 191 
 192         return 1;
 193 }
 194 
 195 static int find_lnx1_partitions(struct parsed_partitions *state,
 196                                 struct hd_geometry *geo,
 197                                 int blocksize,
 198                                 char name[],
 199                                 union label_t *label,
 200                                 sector_t labelsect,
 201                                 loff_t i_size,
 202                                 dasd_information2_t *info)
 203 {
 204         loff_t offset, geo_size, size;
 205         char tmp[64];
 206         int secperblk;
 207 
 208         snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
 209         strlcat(state->pp_buf, tmp, PAGE_SIZE);
 210         secperblk = blocksize >> 9;
 211         if (label->lnx.ldl_version == 0xf2) {
 212                 size = label->lnx.formatted_blocks * secperblk;
 213         } else {
 214                 /*
 215                  * Formated w/o large volume support. If the sanity check
 216                  * 'size based on geo == size based on i_size' is true, then
 217                  * we can safely assume that we know the formatted size of
 218                  * the disk, otherwise we need additional information
 219                  * that we can only get from a real DASD device.
 220                  */
 221                 geo_size = geo->cylinders * geo->heads
 222                         * geo->sectors * secperblk;
 223                 size = i_size >> 9;
 224                 if (size != geo_size) {
 225                         if (!info) {
 226                                 strlcat(state->pp_buf, "\n", PAGE_SIZE);
 227                                 return 1;
 228                         }
 229                         if (!strcmp(info->type, "ECKD"))
 230                                 if (geo_size < size)
 231                                         size = geo_size;
 232                         /* else keep size based on i_size */
 233                 }
 234         }
 235         /* first and only partition starts in the first block after the label */
 236         offset = labelsect + secperblk;
 237         put_partition(state, 1, offset, size - offset);
 238         strlcat(state->pp_buf, "\n", PAGE_SIZE);
 239         return 1;
 240 }
 241 
 242 static int find_cms1_partitions(struct parsed_partitions *state,
 243                                 struct hd_geometry *geo,
 244                                 int blocksize,
 245                                 char name[],
 246                                 union label_t *label,
 247                                 sector_t labelsect)
 248 {
 249         loff_t offset, size;
 250         char tmp[64];
 251         int secperblk;
 252 
 253         /*
 254          * VM style CMS1 labeled disk
 255          */
 256         blocksize = label->cms.block_size;
 257         secperblk = blocksize >> 9;
 258         if (label->cms.disk_offset != 0) {
 259                 snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
 260                 strlcat(state->pp_buf, tmp, PAGE_SIZE);
 261                 /* disk is reserved minidisk */
 262                 offset = label->cms.disk_offset * secperblk;
 263                 size = (label->cms.block_count - 1) * secperblk;
 264         } else {
 265                 snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
 266                 strlcat(state->pp_buf, tmp, PAGE_SIZE);
 267                 /*
 268                  * Special case for FBA devices:
 269                  * If an FBA device is CMS formatted with blocksize > 512 byte
 270                  * and the DIAG discipline is used, then the CMS label is found
 271                  * in sector 1 instead of block 1. However, the partition is
 272                  * still supposed to start in block 2.
 273                  */
 274                 if (labelsect == 1)
 275                         offset = 2 * secperblk;
 276                 else
 277                         offset = labelsect + secperblk;
 278                 size = label->cms.block_count * secperblk;
 279         }
 280 
 281         put_partition(state, 1, offset, size-offset);
 282         strlcat(state->pp_buf, "\n", PAGE_SIZE);
 283         return 1;
 284 }
 285 
 286 
 287 /*
 288  * This is the main function, called by check.c
 289  */
 290 int ibm_partition(struct parsed_partitions *state)
 291 {
 292         struct block_device *bdev = state->bdev;
 293         int blocksize, res;
 294         loff_t i_size, offset, size;
 295         dasd_information2_t *info;
 296         struct hd_geometry *geo;
 297         char type[5] = {0,};
 298         char name[7] = {0,};
 299         sector_t labelsect;
 300         union label_t *label;
 301 
 302         res = 0;
 303         blocksize = bdev_logical_block_size(bdev);
 304         if (blocksize <= 0)
 305                 goto out_exit;
 306         i_size = i_size_read(bdev->bd_inode);
 307         if (i_size == 0)
 308                 goto out_exit;
 309         info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
 310         if (info == NULL)
 311                 goto out_exit;
 312         geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL);
 313         if (geo == NULL)
 314                 goto out_nogeo;
 315         label = kmalloc(sizeof(union label_t), GFP_KERNEL);
 316         if (label == NULL)
 317                 goto out_nolab;
 318         if (ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
 319                 goto out_freeall;
 320         if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0) {
 321                 kfree(info);
 322                 info = NULL;
 323         }
 324 
 325         if (find_label(state, info, geo, blocksize, &labelsect, name, type,
 326                        label)) {
 327                 if (!strncmp(type, "VOL1", 4)) {
 328                         res = find_vol1_partitions(state, geo, blocksize, name,
 329                                                    label);
 330                 } else if (!strncmp(type, "LNX1", 4)) {
 331                         res = find_lnx1_partitions(state, geo, blocksize, name,
 332                                                    label, labelsect, i_size,
 333                                                    info);
 334                 } else if (!strncmp(type, "CMS1", 4)) {
 335                         res = find_cms1_partitions(state, geo, blocksize, name,
 336                                                    label, labelsect);
 337                 }
 338         } else if (info) {
 339                 /*
 340                  * ugly but needed for backward compatibility:
 341                  * If the block device is a DASD (i.e. BIODASDINFO2 works),
 342                  * then we claim it in any case, even though it has no valid
 343                  * label. If it has the LDL format, then we simply define a
 344                  * partition as if it had an LNX1 label.
 345                  */
 346                 res = 1;
 347                 if (info->format == DASD_FORMAT_LDL) {
 348                         strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
 349                         size = i_size >> 9;
 350                         offset = (info->label_block + 1) * (blocksize >> 9);
 351                         put_partition(state, 1, offset, size-offset);
 352                         strlcat(state->pp_buf, "\n", PAGE_SIZE);
 353                 }
 354         } else
 355                 res = 0;
 356 
 357 out_freeall:
 358         kfree(label);
 359 out_nolab:
 360         kfree(geo);
 361 out_nogeo:
 362         kfree(info);
 363 out_exit:
 364         return res;
 365 }

/* [<][>][^][v][top][bottom][index][help] */