Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
0004  *                  Volker Sameske <sameske@de.ibm.com>
0005  * Bugreports.to..: <Linux390@de.ibm.com>
0006  * Copyright IBM Corp. 1999, 2012
0007  */
0008 
0009 #include <linux/buffer_head.h>
0010 #include <linux/hdreg.h>
0011 #include <linux/slab.h>
0012 #include <asm/dasd.h>
0013 #include <asm/ebcdic.h>
0014 #include <linux/uaccess.h>
0015 #include <asm/vtoc.h>
0016 #include <linux/module.h>
0017 #include <linux/dasd_mod.h>
0018 
0019 #include "check.h"
0020 
0021 union label_t {
0022     struct vtoc_volume_label_cdl vol;
0023     struct vtoc_volume_label_ldl lnx;
0024     struct vtoc_cms_label cms;
0025 };
0026 
0027 /*
0028  * compute the block number from a
0029  * cyl-cyl-head-head structure
0030  */
0031 static sector_t cchh2blk(struct vtoc_cchh *ptr, struct hd_geometry *geo)
0032 {
0033     sector_t cyl;
0034     __u16 head;
0035 
0036     /* decode cylinder and heads for large volumes */
0037     cyl = ptr->hh & 0xFFF0;
0038     cyl <<= 12;
0039     cyl |= ptr->cc;
0040     head = ptr->hh & 0x000F;
0041     return cyl * geo->heads * geo->sectors +
0042            head * geo->sectors;
0043 }
0044 
0045 /*
0046  * compute the block number from a
0047  * cyl-cyl-head-head-block structure
0048  */
0049 static sector_t cchhb2blk(struct vtoc_cchhb *ptr, struct hd_geometry *geo)
0050 {
0051     sector_t cyl;
0052     __u16 head;
0053 
0054     /* decode cylinder and heads for large volumes */
0055     cyl = ptr->hh & 0xFFF0;
0056     cyl <<= 12;
0057     cyl |= ptr->cc;
0058     head = ptr->hh & 0x000F;
0059     return  cyl * geo->heads * geo->sectors +
0060         head * geo->sectors +
0061         ptr->b;
0062 }
0063 
0064 static int find_label(struct parsed_partitions *state,
0065               dasd_information2_t *info,
0066               struct hd_geometry *geo,
0067               int blocksize,
0068               sector_t *labelsect,
0069               char name[],
0070               char type[],
0071               union label_t *label)
0072 {
0073     Sector sect;
0074     unsigned char *data;
0075     sector_t testsect[3];
0076     unsigned char temp[5];
0077     int found = 0;
0078     int i, testcount;
0079 
0080     /* There a three places where we may find a valid label:
0081      * - on an ECKD disk it's block 2
0082      * - on an FBA disk it's block 1
0083      * - on an CMS formatted FBA disk it is sector 1, even if the block size
0084      *   is larger than 512 bytes (possible if the DIAG discipline is used)
0085      * If we have a valid info structure, then we know exactly which case we
0086      * have, otherwise we just search through all possebilities.
0087      */
0088     if (info) {
0089         if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
0090             (info->cu_type == 0x3880 && info->dev_type == 0x3370))
0091             testsect[0] = info->label_block;
0092         else
0093             testsect[0] = info->label_block * (blocksize >> 9);
0094         testcount = 1;
0095     } else {
0096         testsect[0] = 1;
0097         testsect[1] = (blocksize >> 9);
0098         testsect[2] = 2 * (blocksize >> 9);
0099         testcount = 3;
0100     }
0101     for (i = 0; i < testcount; ++i) {
0102         data = read_part_sector(state, testsect[i], &sect);
0103         if (data == NULL)
0104             continue;
0105         memcpy(label, data, sizeof(*label));
0106         memcpy(temp, data, 4);
0107         temp[4] = 0;
0108         EBCASC(temp, 4);
0109         put_dev_sector(sect);
0110         if (!strcmp(temp, "VOL1") ||
0111             !strcmp(temp, "LNX1") ||
0112             !strcmp(temp, "CMS1")) {
0113             if (!strcmp(temp, "VOL1")) {
0114                 strncpy(type, label->vol.vollbl, 4);
0115                 strncpy(name, label->vol.volid, 6);
0116             } else {
0117                 strncpy(type, label->lnx.vollbl, 4);
0118                 strncpy(name, label->lnx.volid, 6);
0119             }
0120             EBCASC(type, 4);
0121             EBCASC(name, 6);
0122             *labelsect = testsect[i];
0123             found = 1;
0124             break;
0125         }
0126     }
0127     if (!found)
0128         memset(label, 0, sizeof(*label));
0129 
0130     return found;
0131 }
0132 
0133 static int find_vol1_partitions(struct parsed_partitions *state,
0134                 struct hd_geometry *geo,
0135                 int blocksize,
0136                 char name[],
0137                 union label_t *label)
0138 {
0139     sector_t blk;
0140     int counter;
0141     char tmp[64];
0142     Sector sect;
0143     unsigned char *data;
0144     loff_t offset, size;
0145     struct vtoc_format1_label f1;
0146     int secperblk;
0147 
0148     snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
0149     strlcat(state->pp_buf, tmp, PAGE_SIZE);
0150     /*
0151      * get start of VTOC from the disk label and then search for format1
0152      * and format8 labels
0153      */
0154     secperblk = blocksize >> 9;
0155     blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
0156     counter = 0;
0157     data = read_part_sector(state, blk * secperblk, &sect);
0158     while (data != NULL) {
0159         memcpy(&f1, data, sizeof(struct vtoc_format1_label));
0160         put_dev_sector(sect);
0161         /* skip FMT4 / FMT5 / FMT7 labels */
0162         if (f1.DS1FMTID == _ascebc['4']
0163             || f1.DS1FMTID == _ascebc['5']
0164             || f1.DS1FMTID == _ascebc['7']
0165             || f1.DS1FMTID == _ascebc['9']) {
0166             blk++;
0167             data = read_part_sector(state, blk * secperblk, &sect);
0168             continue;
0169         }
0170         /* only FMT1 and 8 labels valid at this point */
0171         if (f1.DS1FMTID != _ascebc['1'] &&
0172             f1.DS1FMTID != _ascebc['8'])
0173             break;
0174         /* OK, we got valid partition data */
0175         offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
0176         size  = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
0177             offset + geo->sectors;
0178         offset *= secperblk;
0179         size *= secperblk;
0180         if (counter >= state->limit)
0181             break;
0182         put_partition(state, counter + 1, offset, size);
0183         counter++;
0184         blk++;
0185         data = read_part_sector(state, blk * secperblk, &sect);
0186     }
0187     strlcat(state->pp_buf, "\n", PAGE_SIZE);
0188 
0189     if (!data)
0190         return -1;
0191 
0192     return 1;
0193 }
0194 
0195 static int find_lnx1_partitions(struct parsed_partitions *state,
0196                 struct hd_geometry *geo,
0197                 int blocksize,
0198                 char name[],
0199                 union label_t *label,
0200                 sector_t labelsect,
0201                 sector_t nr_sectors,
0202                 dasd_information2_t *info)
0203 {
0204     loff_t offset, geo_size, size;
0205     char tmp[64];
0206     int secperblk;
0207 
0208     snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
0209     strlcat(state->pp_buf, tmp, PAGE_SIZE);
0210     secperblk = blocksize >> 9;
0211     if (label->lnx.ldl_version == 0xf2) {
0212         size = label->lnx.formatted_blocks * secperblk;
0213     } else {
0214         /*
0215          * Formated w/o large volume support. If the sanity check
0216          * 'size based on geo == size based on nr_sectors' is true, then
0217          * we can safely assume that we know the formatted size of
0218          * the disk, otherwise we need additional information
0219          * that we can only get from a real DASD device.
0220          */
0221         geo_size = geo->cylinders * geo->heads
0222             * geo->sectors * secperblk;
0223         size = nr_sectors;
0224         if (size != geo_size) {
0225             if (!info) {
0226                 strlcat(state->pp_buf, "\n", PAGE_SIZE);
0227                 return 1;
0228             }
0229             if (!strcmp(info->type, "ECKD"))
0230                 if (geo_size < size)
0231                     size = geo_size;
0232             /* else keep size based on nr_sectors */
0233         }
0234     }
0235     /* first and only partition starts in the first block after the label */
0236     offset = labelsect + secperblk;
0237     put_partition(state, 1, offset, size - offset);
0238     strlcat(state->pp_buf, "\n", PAGE_SIZE);
0239     return 1;
0240 }
0241 
0242 static int find_cms1_partitions(struct parsed_partitions *state,
0243                 struct hd_geometry *geo,
0244                 int blocksize,
0245                 char name[],
0246                 union label_t *label,
0247                 sector_t labelsect)
0248 {
0249     loff_t offset, size;
0250     char tmp[64];
0251     int secperblk;
0252 
0253     /*
0254      * VM style CMS1 labeled disk
0255      */
0256     blocksize = label->cms.block_size;
0257     secperblk = blocksize >> 9;
0258     if (label->cms.disk_offset != 0) {
0259         snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
0260         strlcat(state->pp_buf, tmp, PAGE_SIZE);
0261         /* disk is reserved minidisk */
0262         offset = label->cms.disk_offset * secperblk;
0263         size = (label->cms.block_count - 1) * secperblk;
0264     } else {
0265         snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
0266         strlcat(state->pp_buf, tmp, PAGE_SIZE);
0267         /*
0268          * Special case for FBA devices:
0269          * If an FBA device is CMS formatted with blocksize > 512 byte
0270          * and the DIAG discipline is used, then the CMS label is found
0271          * in sector 1 instead of block 1. However, the partition is
0272          * still supposed to start in block 2.
0273          */
0274         if (labelsect == 1)
0275             offset = 2 * secperblk;
0276         else
0277             offset = labelsect + secperblk;
0278         size = label->cms.block_count * secperblk;
0279     }
0280 
0281     put_partition(state, 1, offset, size-offset);
0282     strlcat(state->pp_buf, "\n", PAGE_SIZE);
0283     return 1;
0284 }
0285 
0286 
0287 /*
0288  * This is the main function, called by check.c
0289  */
0290 int ibm_partition(struct parsed_partitions *state)
0291 {
0292     int (*fn)(struct gendisk *disk, dasd_information2_t *info);
0293     struct gendisk *disk = state->disk;
0294     struct block_device *bdev = disk->part0;
0295     int blocksize, res;
0296     loff_t offset, size;
0297     sector_t nr_sectors;
0298     dasd_information2_t *info;
0299     struct hd_geometry *geo;
0300     char type[5] = {0,};
0301     char name[7] = {0,};
0302     sector_t labelsect;
0303     union label_t *label;
0304 
0305     res = 0;
0306     if (!disk->fops->getgeo)
0307         goto out_exit;
0308     fn = symbol_get(dasd_biodasdinfo);
0309     blocksize = bdev_logical_block_size(bdev);
0310     if (blocksize <= 0)
0311         goto out_symbol;
0312     nr_sectors = bdev_nr_sectors(bdev);
0313     if (nr_sectors == 0)
0314         goto out_symbol;
0315     info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
0316     if (info == NULL)
0317         goto out_symbol;
0318     geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL);
0319     if (geo == NULL)
0320         goto out_nogeo;
0321     label = kmalloc(sizeof(union label_t), GFP_KERNEL);
0322     if (label == NULL)
0323         goto out_nolab;
0324     /* set start if not filled by getgeo function e.g. virtblk */
0325     geo->start = get_start_sect(bdev);
0326     if (disk->fops->getgeo(bdev, geo))
0327         goto out_freeall;
0328     if (!fn || fn(disk, info)) {
0329         kfree(info);
0330         info = NULL;
0331     }
0332 
0333     if (find_label(state, info, geo, blocksize, &labelsect, name, type,
0334                label)) {
0335         if (!strncmp(type, "VOL1", 4)) {
0336             res = find_vol1_partitions(state, geo, blocksize, name,
0337                            label);
0338         } else if (!strncmp(type, "LNX1", 4)) {
0339             res = find_lnx1_partitions(state, geo, blocksize, name,
0340                            label, labelsect, nr_sectors,
0341                            info);
0342         } else if (!strncmp(type, "CMS1", 4)) {
0343             res = find_cms1_partitions(state, geo, blocksize, name,
0344                            label, labelsect);
0345         }
0346     } else if (info) {
0347         /*
0348          * ugly but needed for backward compatibility:
0349          * If the block device is a DASD (i.e. BIODASDINFO2 works),
0350          * then we claim it in any case, even though it has no valid
0351          * label. If it has the LDL format, then we simply define a
0352          * partition as if it had an LNX1 label.
0353          */
0354         res = 1;
0355         if (info->format == DASD_FORMAT_LDL) {
0356             strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
0357             size = nr_sectors;
0358             offset = (info->label_block + 1) * (blocksize >> 9);
0359             put_partition(state, 1, offset, size-offset);
0360             strlcat(state->pp_buf, "\n", PAGE_SIZE);
0361         }
0362     } else
0363         res = 0;
0364 
0365 out_freeall:
0366     kfree(label);
0367 out_nolab:
0368     kfree(geo);
0369 out_nogeo:
0370     kfree(info);
0371 out_symbol:
0372     if (fn)
0373         symbol_put(dasd_biodasdinfo);
0374 out_exit:
0375     return res;
0376 }