1e064c4a4f86017ed8fa8832fee08c2224cd09b6
[linux-2.6.git] / fs / partitions / ibm.c
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 inline int
25 cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) {
26         return ptr->cc * geo->heads * geo->sectors +
27                ptr->hh * geo->sectors;
28 }
29
30 /*
31  * compute the block number from a
32  * cyl-cyl-head-head-block structure
33  */
34 static inline int
35 cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) {
36         return ptr->cc * geo->heads * geo->sectors +
37                 ptr->hh * geo->sectors +
38                 ptr->b;
39 }
40
41 /*
42  */
43 int
44 ibm_partition(struct parsed_partitions *state, struct block_device *bdev)
45 {
46         int blocksize, offset, size,res;
47         loff_t i_size;
48         dasd_information2_t *info;
49         struct hd_geometry *geo;
50         char type[5] = {0,};
51         char name[7] = {0,};
52         union label_t {
53                 struct vtoc_volume_label vol;
54                 struct vtoc_cms_label cms;
55         } *label;
56         unsigned char *data;
57         Sector sect;
58
59         res = 0;
60         blocksize = bdev_hardsect_size(bdev);
61         if (blocksize <= 0)
62                 goto out_exit;
63         i_size = i_size_read(bdev->bd_inode);
64         if (i_size == 0)
65                 goto out_exit;
66
67         info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
68         if (info == NULL)
69                 goto out_exit;
70         geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL);
71         if (geo == NULL)
72                 goto out_nogeo;
73         label = kmalloc(sizeof(union label_t), GFP_KERNEL);
74         if (label == NULL)
75                 goto out_nolab;
76
77         if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0 ||
78             ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
79                 goto out_freeall;
80
81         /*
82          * Get volume label, extract name and type.
83          */
84         data = read_dev_sector(bdev, info->label_block*(blocksize/512), &sect);
85         if (data == NULL)
86                 goto out_readerr;
87
88         strncpy (type, data, 4);
89         if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD")))
90                 strncpy(name, data + 8, 6);
91         else
92                 strncpy(name, data + 4, 6);
93         memcpy(label, data, sizeof(union label_t));
94         put_dev_sector(sect);
95
96         EBCASC(type, 4);
97         EBCASC(name, 6);
98
99         res = 1;
100
101         /*
102          * Three different formats: LDL, CDL and unformated disk
103          *
104          * identified by info->format
105          *
106          * unformated disks we do not have to care about
107          */
108         if (info->format == DASD_FORMAT_LDL) {
109                 if (strncmp(type, "CMS1", 4) == 0) {
110                         /*
111                          * VM style CMS1 labeled disk
112                          */
113                         if (label->cms.disk_offset != 0) {
114                                 printk("CMS1/%8s(MDSK):", name);
115                                 /* disk is reserved minidisk */
116                                 blocksize = label->cms.block_size;
117                                 offset = label->cms.disk_offset;
118                                 size = (label->cms.block_count - 1)
119                                         * (blocksize >> 9);
120                         } else {
121                                 printk("CMS1/%8s:", name);
122                                 offset = (info->label_block + 1);
123                                 size = i_size >> 9;
124                         }
125                 } else {
126                         /*
127                          * Old style LNX1 or unlabeled disk
128                          */
129                         if (strncmp(type, "LNX1", 4) == 0)
130                                 printk ("LNX1/%8s:", name);
131                         else
132                                 printk("(nonl)");
133                         offset = (info->label_block + 1);
134                         size = i_size >> 9;
135                 }
136                 put_partition(state, 1, offset*(blocksize >> 9),
137                                       size-offset*(blocksize >> 9));
138         } else if (info->format == DASD_FORMAT_CDL) {
139                 /*
140                  * New style CDL formatted disk
141                  */
142                 unsigned int blk;
143                 int counter;
144
145                 /*
146                  * check if VOL1 label is available
147                  * if not, something is wrong, skipping partition detection
148                  */
149                 if (strncmp(type, "VOL1",  4) == 0) {
150                         printk("VOL1/%8s:", name);
151                         /*
152                          * get block number and read then go through format1
153                          * labels
154                          */
155                         blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
156                         counter = 0;
157                         data = read_dev_sector(bdev, blk * (blocksize/512),
158                                                &sect);
159                         while (data != NULL) {
160                                 struct vtoc_format1_label f1;
161
162                                 memcpy(&f1, data,
163                                        sizeof(struct vtoc_format1_label));
164                                 put_dev_sector(sect);
165
166                                 /* skip FMT4 / FMT5 / FMT7 labels */
167                                 if (f1.DS1FMTID == _ascebc['4']
168                                     || f1.DS1FMTID == _ascebc['5']
169                                     || f1.DS1FMTID == _ascebc['7']) {
170                                         blk++;
171                                         data = read_dev_sector(bdev, blk *
172                                                                (blocksize/512),
173                                                                 &sect);
174                                         continue;
175                                 }
176
177                                 /* only FMT1 valid at this point */
178                                 if (f1.DS1FMTID != _ascebc['1'])
179                                         break;
180
181                                 /* OK, we got valid partition data */
182                                 offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
183                                 size  = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
184                                         offset + geo->sectors;
185                                 if (counter >= state->limit)
186                                         break;
187                                 put_partition(state, counter + 1,
188                                               offset * (blocksize >> 9),
189                                               size * (blocksize >> 9));
190                                 counter++;
191                                 blk++;
192                                 data = read_dev_sector(bdev,
193                                                        blk * (blocksize/512),
194                                                        &sect);
195                         }
196
197                         if (!data)
198                                 /* Are we not supposed to report this ? */
199                                 goto out_readerr;
200                 } else
201                         printk(KERN_WARNING "Warning, expected Label VOL1 not "
202                                "found, treating as CDL formated Disk");
203
204         }
205
206         printk("\n");
207         goto out_freeall;
208
209
210 out_readerr:
211         res = -1;
212 out_freeall:
213         kfree(label);
214 out_nolab:
215         kfree(geo);
216 out_nogeo:
217         kfree(info);
218 out_exit:
219         return res;
220 }