~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/block/partitions/ibm.c

Version: ~ [ linux-5.6-rc3 ] ~ [ linux-5.5.6 ] ~ [ linux-5.4.22 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.106 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.171 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.214 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.214 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.82 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp