xref: /openbmc/linux/block/partitions/ibm.c (revision 37185b33)
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 sector_t
25 cchh2blk (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  */
43 static sector_t
44 cchhb2blk (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  */
61 int 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_INFO "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 
265 out_readerr:
266 	res = -1;
267 out_freeall:
268 	kfree(label);
269 out_nolab:
270 	kfree(geo);
271 out_nogeo:
272 	kfree(info);
273 out_exit:
274 	return res;
275 }
276