File: /usr/src/linux/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      * History of changes (starts July 2000)
9      * 07/10/00 Fixed detection of CMS formatted disks     
10      * 02/13/00 VTOC partition support added
11      */
12     
13     #include <linux/config.h>
14     #include <linux/fs.h>
15     #include <linux/genhd.h>
16     #include <linux/kernel.h>
17     #include <linux/major.h>
18     #include <linux/string.h>
19     #include <linux/blk.h>
20     #include <linux/slab.h>
21     #include <linux/hdreg.h>
22     #include <linux/ioctl.h>
23     #include <linux/version.h>
24     #include <asm/ebcdic.h>
25     #include <asm/uaccess.h>
26     #include <asm/dasd.h>
27     
28     #include "ibm.h"
29     #include "check.h"
30     #include <asm/vtoc.h>
31     
32     typedef enum {
33       ibm_partition_lnx1 = 0,
34       ibm_partition_vol1 = 1,
35       ibm_partition_cms1 = 2,
36       ibm_partition_none = 3
37     } ibm_partition_t;
38     
39     static char* part_names[] = {   [ibm_partition_lnx1] = "LNX1",
40     			     [ibm_partition_vol1] = "VOL1",
41     			     [ibm_partition_cms1] = "CMS1",
42     			     [ibm_partition_none] = "(nonl)"
43     };
44     
45     static int
46     get_drive_geometry(int kdev,struct hd_geometry *geo) 
47     {
48     	struct block_device *bdev = bdget(kdev_t_to_nr(kdev));
49     	int rc = blkdev_get(bdev, 0, 1, BDEV_FILE);
50             if ( rc == 0 ) {
51     		rc = ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo);
52     		blkdev_put(bdev, BDEV_FILE);
53     	}
54     	return rc;
55     }
56     
57     static int
58     get_drive_info(int kdev,dasd_information_t *info) 
59     {
60     	struct block_device *bdev = bdget(kdev_t_to_nr(kdev));
61     	int rc = blkdev_get(bdev, 0, 1, BDEV_FILE);
62             if ( rc == 0 ) {
63     		rc = ioctl_by_bdev(bdev, BIODASDINFO, (unsigned long)(info));
64     		blkdev_put(bdev, BDEV_FILE);
65     	}
66     	return rc;
67     }
68     
69     static ibm_partition_t
70     get_partition_type ( char * type )
71     {
72     	int i;
73     	for ( i = 0; i < 3; i ++) {
74     		if ( ! strncmp (type,part_names[i],4) ) 
75     			break;
76     	}
77             return i;
78     }
79     
80     /*
81      * add the two default partitions
82      * - whole dasd
83      * - whole dasd without "offset"
84      */
85     static inline void
86     two_partitions(struct gendisk *hd,
87     	       int minor,
88     	       int blocksize,
89     	       int offset,
90     	       int size) {
91     
92             add_gd_partition( hd, minor, 0,size);
93     	add_gd_partition( hd, minor + 1, 
94     			   offset * (blocksize >> 9),
95     			   size-offset*(blocksize>>9));
96     }
97     
98     
99     /*
100      * compute the block number from a 
101      * cyl-cyl-head-head structure
102      */
103     static inline int
104     cchh2blk (cchh_t *ptr, struct hd_geometry *geo) {
105             return ptr->cc * geo->heads * geo->sectors +
106     	       ptr->hh * geo->sectors;
107     }
108     
109     
110     /*
111      * compute the block number from a 
112      * cyl-cyl-head-head-block structure
113      */
114     static inline int
115     cchhb2blk (cchhb_t *ptr, struct hd_geometry *geo) {
116             return ptr->cc * geo->heads * geo->sectors +
117     		ptr->hh * geo->sectors +
118     		ptr->b;
119     }
120     
121     int 
122     ibm_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int
123     first_part_minor)
124     {
125     	struct buffer_head *bh, *buf;
126     	ibm_partition_t partition_type;
127     	char type[5] = {0,};
128     	char name[7] = {0,};
129     	struct hd_geometry *geo;
130     	int blocksize;
131     	int offset=0, size=0, psize=0, counter=0;
132     	unsigned int blk;
133     	format1_label_t f1;
134     	volume_label_t vlabel;
135     	dasd_information_t *info;
136     
137     	if ( first_sector != 0 ) {
138     		BUG();
139     	}
140     	info = (struct dasd_information_t *)kmalloc(sizeof(dasd_information_t),
141     						    GFP_KERNEL);
142     	if ( info == NULL )
143     		return 0;
144     	if (get_drive_info (dev,info)) 
145     		return 0;
146     	geo = (struct hd_geometry *)kmalloc(sizeof(struct hd_geometry),
147     					    GFP_KERNEL);
148     	if ( geo == NULL )
149     		return 0;
150     	if (get_drive_geometry (dev,geo)) 
151     		return 0;
152     	blocksize = hardsect_size[MAJOR(dev)][MINOR(dev)];
153     	if ( blocksize <= 0 ) {
154     		return 0;
155     	}
156     	
157     	set_blocksize(dev, blocksize);  /* OUCH !! */
158     	if ( ( bh = bread( dev, info->label_block, blocksize) ) != NULL ) {
159     		strncpy ( type,bh -> b_data + 0, 4);
160     		if ((!info->FBA_layout) && (!strcmp(info->type,"ECKD"))) {
161     		        
162     		        strncpy ( name,bh -> b_data + 8, 6);
163     		} else {
164     		        strncpy ( name,bh -> b_data + 4, 6);		  
165     		}
166     		memcpy (&vlabel, bh->b_data, sizeof(volume_label_t));
167             } else {
168     		return 0;
169     	}
170     	EBCASC(type,4);
171     	EBCASC(name,6);
172     	
173     	partition_type = get_partition_type(type);
174     	printk ( "%4s/%8s:",part_names[partition_type],name);
175     	switch ( partition_type ) {
176     	case ibm_partition_cms1:
177     		if (* (((long *)bh->b_data) + 13) != 0) {
178     			/* disk is reserved minidisk */
179     			long *label=(long*)bh->b_data;
180     			blocksize = label[3];
181     			offset = label[13];
182     			size = (label[7]-1)*(blocksize>>9); 
183     			printk ("(MDSK)");
184     		} else {
185     			offset = (info->label_block + 1);
186     			size = hd -> sizes[MINOR(dev)]<<1;
187     		}
188     		two_partitions( hd, MINOR(dev), blocksize, offset, size);
189     		break;
190     	case ibm_partition_lnx1: 
191     	case ibm_partition_none:
192     		offset = (info->label_block + 1);
193     		size = hd -> sizes[MINOR(dev)]<<1;
194     		two_partitions( hd, MINOR(dev), blocksize, offset, size);
195     		break;
196     	case ibm_partition_vol1: 
197     		size = hd -> sizes[MINOR(dev)]<<1;
198     		add_gd_partition(hd, MINOR(dev), 0, size);
199     		
200     		/* get block number and read then first format1 label */
201     		blk = cchhb2blk(&vlabel.vtoc, geo) + 1;
202     		if ((buf = bread( dev, blk, blocksize)) != NULL) {
203     		        memcpy (&f1, buf->b_data, sizeof(format1_label_t));
204     			bforget(buf);
205     		}
206     		
207     		while (f1.DS1FMTID == _ascebc['1']) {
208     		        offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
209     			psize  = cchh2blk(&f1.DS1EXT1.ulimit, geo) - 
210     				offset + geo->sectors;
211     			
212     			counter++;
213     			add_gd_partition(hd, MINOR(dev) + counter, 
214     					 offset * (blocksize >> 9),
215     					 psize * (blocksize >> 9));
216     			
217     			blk++;
218     			if ((buf = bread( dev, blk, blocksize)) != NULL) {
219     			        memcpy (&f1, buf->b_data, 
220     					sizeof(format1_label_t));
221     				bforget(buf);
222     			}
223     		}
224     		break;
225     	default:
226     		add_gd_partition( hd, MINOR(dev), 0, 0);
227     		add_gd_partition( hd, MINOR(dev) + 1, 0, 0);
228     	}
229     	
230     	printk ( "\n" );
231     	bforget(bh);
232     	return 1;
233     }
234