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 inline int
25cchh2blk (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 */
34static inline int
35cchhb2blk (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 */
43int
44ibm_partition(struct parsed_partitions *state, struct block_device *bdev)
45{
46	int blocksize, offset, size,res;
47	loff_t i_size;
48	dasd_information_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	if ((info = kmalloc(sizeof(dasd_information_t), GFP_KERNEL)) == NULL)
68		goto out_exit;
69	if ((geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL)) == NULL)
70		goto out_nogeo;
71	if ((label = kmalloc(sizeof(union label_t), GFP_KERNEL)) == NULL)
72		goto out_nolab;
73
74	if (ioctl_by_bdev(bdev, BIODASDINFO, (unsigned long)info) != 0 ||
75	    ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
76		goto out_freeall;
77
78	/*
79	 * Get volume label, extract name and type.
80	 */
81	data = read_dev_sector(bdev, info->label_block*(blocksize/512), &sect);
82	if (data == NULL)
83		goto out_readerr;
84
85	strncpy (type, data, 4);
86	if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD")))
87		strncpy(name, data + 8, 6);
88	else
89		strncpy(name, data + 4, 6);
90	memcpy(label, data, sizeof(union label_t));
91	put_dev_sector(sect);
92
93	EBCASC(type, 4);
94	EBCASC(name, 6);
95
96	res = 1;
97
98	/*
99	 * Three different types: CMS1, VOL1 and LNX1/unlabeled
100	 */
101	if (strncmp(type, "CMS1", 4) == 0) {
102		/*
103		 * VM style CMS1 labeled disk
104		 */
105		if (label->cms.disk_offset != 0) {
106			printk("CMS1/%8s(MDSK):", name);
107			/* disk is reserved minidisk */
108			blocksize = label->cms.block_size;
109			offset = label->cms.disk_offset;
110			size = (label->cms.block_count - 1) * (blocksize >> 9);
111		} else {
112			printk("CMS1/%8s:", name);
113			offset = (info->label_block + 1);
114			size = i_size >> 9;
115		}
116		put_partition(state, 1, offset*(blocksize >> 9),
117				 size-offset*(blocksize >> 9));
118	} else if ((strncmp(type, "VOL1", 4) == 0) &&
119		(!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
120		/*
121		 * New style VOL1 labeled disk
122		 */
123		unsigned int blk;
124		int counter;
125
126		printk("VOL1/%8s:", name);
127
128		/* get block number and read then go through format1 labels */
129		blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
130		counter = 0;
131		while ((data = read_dev_sector(bdev, blk*(blocksize/512),
132					       &sect)) != NULL) {
133			struct vtoc_format1_label f1;
134
135			memcpy(&f1, data, sizeof(struct vtoc_format1_label));
136			put_dev_sector(sect);
137
138			/* skip FMT4 / FMT5 / FMT7 labels */
139			if (f1.DS1FMTID == _ascebc['4']
140			    || f1.DS1FMTID == _ascebc['5']
141			    || f1.DS1FMTID == _ascebc['7']) {
142			        blk++;
143				continue;
144			}
145
146			/* only FMT1 valid at this point */
147			if (f1.DS1FMTID != _ascebc['1'])
148				break;
149
150			/* OK, we got valid partition data */
151		        offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
152			size  = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
153				offset + geo->sectors;
154			if (counter >= state->limit)
155				break;
156			put_partition(state, counter + 1,
157				      offset * (blocksize >> 9),
158				      size * (blocksize >> 9));
159			counter++;
160			blk++;
161		}
162		if (!data)
163		/* Are we not supposed to report this ? */
164			goto out_readerr;
165	} else {
166		/*
167		 * Old style LNX1 or unlabeled disk
168		 */
169		if (strncmp(type, "LNX1", 4) == 0)
170			printk ("LNX1/%8s:", name);
171		else
172			printk("(nonl)/%8s:", name);
173		offset = (info->label_block + 1);
174		size = i_size >> 9;
175		put_partition(state, 1, offset*(blocksize >> 9),
176			      size-offset*(blocksize >> 9));
177	}
178
179	printk("\n");
180	goto out_freeall;
181
182
183out_readerr:
184	res = -1;
185out_freeall:
186	kfree(label);
187out_nolab:
188	kfree(geo);
189out_nogeo:
190	kfree(info);
191out_exit:
192	return res;
193}
194