linux/block/partitions/ibm.c
<<
>>
Prefs
   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 */
  24static sector_t
  25cchh2blk (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 */
  43static sector_t
  44cchhb2blk (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 */
  61int 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, &sect);
 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                                                &sect);
 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), &sect);
 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), &sect);
 250                        }
 251
 252                        if (!data)
 253                                /* Are we not supposed to report this ? */
 254                                goto out_readerr;
 255                } else
 256                        printk(KERN_WARNING "Warning, 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
 265out_readerr:
 266        res = -1;
 267out_freeall:
 268        kfree(label);
 269out_nolab:
 270        kfree(geo);
 271out_nogeo:
 272        kfree(info);
 273out_exit:
 274        return res;
 275}
 276