File: /usr/src/linux/fs/partitions/acorn.c

1     /*
2      *  linux/fs/partitions/acorn.c
3      *
4      *  Copyright (c) 1996-2000 Russell King.
5      *
6      * This program is free software; you can redistribute it and/or modify
7      * it under the terms of the GNU General Public License version 2 as
8      * published by the Free Software Foundation.
9      *
10      *  Scan ADFS partitions on hard disk drives.
11      */
12     #include <linux/config.h>
13     #include <linux/kernel.h>
14     #include <linux/types.h>
15     #include <linux/kdev_t.h>
16     #include <linux/major.h>
17     #include <linux/string.h>
18     #include <linux/genhd.h>
19     #include <linux/fs.h>
20     
21     #include "check.h"
22     #include "acorn.h"
23     
24     static void
25     adfspart_setgeometry(kdev_t dev, unsigned int secspertrack, unsigned int heads,
26     		     unsigned long totalblocks)
27     {
28     	extern void xd_set_geometry(kdev_t dev, unsigned char, unsigned char,
29     				    unsigned long, unsigned int);
30     
31     #ifdef CONFIG_BLK_DEV_MFM
32     	if (MAJOR(dev) == MFM_ACORN_MAJOR)
33     		xd_set_geometry(dev, secspertrack, heads, totalblocks, 1);
34     #endif
35     }
36     
37     static struct adfs_discrecord *
38     adfs_partition(struct gendisk *hd, char *name, char *data,
39     	       unsigned long first_sector, int minor)
40     {
41     	struct adfs_discrecord *dr;
42     	unsigned int nr_sects;
43     
44     	if (adfs_checkbblk(data))
45     		return NULL;
46     
47     	dr = (struct adfs_discrecord *)(data + 0x1c0);
48     
49     	if (dr->disc_size == 0 && dr->disc_size_high == 0)
50     		return NULL;
51     
52     	nr_sects = (le32_to_cpu(dr->disc_size_high) << 23) |
53     		   (le32_to_cpu(dr->disc_size) >> 9);
54     
55     	if (name)
56     		printk(" [%s]", name);
57     	add_gd_partition(hd, minor, first_sector, nr_sects);
58     	return dr;
59     }
60     
61     #ifdef CONFIG_ACORN_PARTITION_RISCIX
62     static int
63     riscix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sect,
64     		 int minor, unsigned long nr_sects)
65     {
66     	struct buffer_head *bh;
67     	struct riscix_record *rr;
68     	unsigned int riscix_minor;
69     	
70     	if(get_ptable_blocksize(dev)!=1024)
71     		return 0;
72     
73     	printk(" [RISCiX]");
74     
75     	add_gd_partition(hd, riscix_minor = minor++, first_sect, nr_sects);
76     	hd->sizes[riscix_minor] = hd->part[riscix_minor].nr_sects >>
77     					(BLOCK_SIZE_BITS - 9);
78     	dev = MKDEV(hd->major, riscix_minor);
79     
80     	if (!(bh = bread(dev, 0, 1024)))
81     		return -1;
82     
83     	rr = (struct riscix_record *)bh->b_data;
84     	if (rr->magic == RISCIX_MAGIC) {
85     		int part;
86     
87     		printk(" <");
88     
89     		for (part = 0; part < 8; part++) {
90     			if (rr->part[part].one &&
91     			    memcmp(rr->part[part].name, "All\0", 4)) {
92     				add_gd_partition(hd, minor++,
93     						le32_to_cpu(rr->part[part].start),
94     						le32_to_cpu(rr->part[part].length));
95     				printk("(%s)", rr->part[part].name);
96     			}
97     		}
98     
99     		printk(" >\n");
100     
101     		if (hd->part[riscix_minor].nr_sects > 2)
102     			hd->part[riscix_minor].nr_sects = 2;
103     	}
104     
105     	brelse(bh);
106     	return minor;
107     }
108     #endif
109     
110     static int
111     linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sect,
112     		int minor, unsigned long nr_sects)
113     {
114     	struct buffer_head *bh;
115     	struct linux_part *linuxp;
116     	unsigned int linux_minor, mask = (1 << hd->minor_shift) - 1;
117     
118     	if(get_ptable_blocksize(dev)!=1024)
119     		return 0;
120     		
121     	printk(" [Linux]");
122     
123     	add_gd_partition(hd, linux_minor = minor++, first_sect, nr_sects);
124     	hd->sizes[linux_minor] = hd->part[linux_minor].nr_sects >>
125     					(BLOCK_SIZE_BITS - 9);
126     	dev = MKDEV(hd->major, linux_minor);
127     
128     	if (!(bh = bread(dev, 0, 1024)))
129     		return -1;
130     
131     	linuxp = (struct linux_part *)bh->b_data;
132     	printk(" <");
133     	while (linuxp->magic == cpu_to_le32(LINUX_NATIVE_MAGIC) ||
134     	       linuxp->magic == cpu_to_le32(LINUX_SWAP_MAGIC)) {
135     		if (!(minor & mask))
136     			break;
137     		add_gd_partition(hd, minor++, first_sect +
138     				 le32_to_cpu(linuxp->start_sect),
139     				 le32_to_cpu(linuxp->nr_sects));
140     		linuxp ++;
141     	}
142     	printk(" >");
143     	/*
144     	 * Prevent someone doing a mkswap or mkfs on this partition
145     	 */
146     	if(hd->part[linux_minor].nr_sects > 2)
147     		hd->part[linux_minor].nr_sects = 2;
148     
149     	brelse(bh);
150     	return minor;
151     }
152     
153     #ifdef CONFIG_ACORN_PARTITION_CUMANA
154     static int
155     adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev,
156     		      unsigned long first_sector, int minor)
157     {
158     	unsigned int start_blk = 0, mask = (1 << hd->minor_shift) - 1;
159     	struct buffer_head *bh = NULL;
160     	char *name = "CUMANA/ADFS";
161     	int first = 1;
162     
163     	if(get_ptable_blocksize(dev)!=1024)
164     		return 0;
165     
166     	/*
167     	 * Try Cumana style partitions - sector 3 contains ADFS boot block
168     	 * with pointer to next 'drive'.
169     	 *
170     	 * There are unknowns in this code - is the 'cylinder number' of the
171     	 * next partition relative to the start of this one - I'm assuming
172     	 * it is.
173     	 *
174     	 * Also, which ID did Cumana use?
175     	 *
176     	 * This is totally unfinished, and will require more work to get it
177     	 * going. Hence it is totally untested.
178     	 */
179     	do {
180     		struct adfs_discrecord *dr;
181     		unsigned int nr_sects;
182     
183     		if (!(minor & mask))
184     			break;
185     
186     		if (!(bh = bread(dev, start_blk + 3, 1024)))
187     			return -1;
188     
189     		dr = adfs_partition(hd, name, bh->b_data,
190     				    first_sector, minor++);
191     		if (!dr)
192     			break;
193     		name = NULL;
194     
195     		nr_sects = (bh->b_data[0x1fd] + (bh->b_data[0x1fe] << 8)) *
196     			   (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) *
197     			   dr->secspertrack;
198     
199     		if (!nr_sects)
200     			break;
201     
202     		first = 0;
203     		first_sector += nr_sects;
204     		start_blk += nr_sects >> (BLOCK_SIZE_BITS - 9);
205     		nr_sects = 0; /* hmm - should be partition size */
206     
207     		switch (bh->b_data[0x1fc] & 15) {
208     		case 0: /* No partition / ADFS? */
209     			break;
210     
211     #ifdef CONFIG_ACORN_PARTITION_RISCIX
212     		case PARTITION_RISCIX_SCSI:
213     			/* RISCiX - we don't know how to find the next one. */
214     			minor = riscix_partition(hd, dev, first_sector,
215     						 minor, nr_sects);
216     			break;
217     #endif
218     
219     		case PARTITION_LINUX:
220     			minor = linux_partition(hd, dev, first_sector,
221     						minor, nr_sects);
222     			break;
223     		}
224     		brelse(bh);
225     		bh = NULL;
226     		if (minor == -1)
227     			return minor;
228     	} while (1);
229     	if (bh)
230     		bforget(bh);
231     	return first ? 0 : 1;
232     }
233     #endif
234     
235     #ifdef CONFIG_ACORN_PARTITION_ADFS
236     /*
237      * Purpose: allocate ADFS partitions.
238      *
239      * Params : hd		- pointer to gendisk structure to store partition info.
240      *	    dev		- device number to access.
241      *	    first_sector- first readable sector on the device.
242      *	    minor	- first available minor on device.
243      *
244      * Returns: -1 on error, 0 for no ADFS boot sector, 1 for ok.
245      *
246      * Alloc  : hda  = whole drive
247      *	    hda1 = ADFS partition on first drive.
248      *	    hda2 = non-ADFS partition.
249      */
250     static int
251     adfspart_check_ADFS(struct gendisk *hd, kdev_t dev,
252     		   unsigned long first_sector, int minor)
253     {
254     	unsigned long start_sect, nr_sects, sectscyl, heads;
255     	struct buffer_head *bh;
256     	struct adfs_discrecord *dr;
257     
258     	if(get_ptable_blocksize(dev)!=1024)
259     		return 0;
260     
261     	if (!(bh = bread(dev, 3, 1024)))
262     		return -1;
263     
264     	dr = adfs_partition(hd, "ADFS", bh->b_data, first_sector, minor++);
265     	if (!dr) {
266     		bforget(bh);
267         		return 0;
268     	}
269     
270     	heads = dr->heads + ((dr->lowsector >> 6) & 1);
271     	adfspart_setgeometry(dev, dr->secspertrack, heads,
272     			     hd->part[MINOR(dev)].nr_sects);
273     	sectscyl = dr->secspertrack * heads;
274     
275     	/*
276     	 * Work out start of non-adfs partition.
277     	 */
278     	start_sect = ((bh->b_data[0x1fe] << 8) + bh->b_data[0x1fd]) * sectscyl;
279     	nr_sects = hd->part[MINOR(dev)].nr_sects - start_sect;
280     
281     	if (start_sect) {
282     		first_sector += start_sect;
283     
284     		switch (bh->b_data[0x1fc] & 15) {
285     #ifdef CONFIG_ACORN_PARTITION_RISCIX
286     		case PARTITION_RISCIX_SCSI:
287     		case PARTITION_RISCIX_MFM:
288     			minor = riscix_partition(hd, dev, first_sector,
289     						 minor, nr_sects);
290     			break;
291     #endif
292     
293     		case PARTITION_LINUX:
294     			minor = linux_partition(hd, dev, first_sector,
295     						minor, nr_sects);
296     			break;
297     		}
298     	}
299     	brelse(bh);
300     	return 1;
301     }
302     #endif
303     
304     #ifdef CONFIG_ACORN_PARTITION_ICS
305     static int adfspart_check_ICSLinux(kdev_t dev, unsigned long block)
306     {
307     	struct buffer_head *bh;
308     	unsigned int offset = block & 1 ? 512 : 0;
309     	int result = 0;
310     
311     	bh = bread(dev, block >> 1, 1024);
312     
313     	if (bh != NULL) {
314     		if (memcmp(bh->b_data + offset, "LinuxPart", 9) == 0)
315     			result = 1;
316     
317     		brelse(bh);
318     	}
319     
320     	return result;
321     }
322     
323     /*
324      * Purpose: allocate ICS partitions.
325      * Params : hd		- pointer to gendisk structure to store partition info.
326      *	    dev		- device number to access.
327      *	    first_sector- first readable sector on the device.
328      *	    minor	- first available minor on device.
329      * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok.
330      * Alloc  : hda  = whole drive
331      *	    hda1 = ADFS partition 0 on first drive.
332      *	    hda2 = ADFS partition 1 on first drive.
333      *		..etc..
334      */
335     static int
336     adfspart_check_ICS(struct gendisk *hd, kdev_t dev,
337     		   unsigned long first_sector, int minor)
338     {
339     	struct buffer_head *bh;
340     	unsigned long sum;
341     	unsigned int i, mask = (1 << hd->minor_shift) - 1;
342     	struct ics_part *p;
343     
344     	if(get_ptable_blocksize(dev)!=1024)
345     		return 0;
346     		
347     	/*
348     	 * Try ICS style partitions - sector 0 contains partition info.
349     	 */
350     	if (!(bh = bread(dev, 0, 1024)))
351     	    	return -1;
352     
353     	/*
354     	 * check for a valid checksum
355     	 */
356     	for (i = 0, sum = 0x50617274; i < 508; i++)
357     		sum += bh->b_data[i];
358     
359     	sum -= le32_to_cpu(*(__u32 *)(&bh->b_data[508]));
360     	if (sum) {
361     	    	bforget(bh);
362     		return 0; /* not ICS partition table */
363     	}
364     
365     	printk(" [ICS]");
366     
367     	for (p = (struct ics_part *)bh->b_data; p->size; p++) {
368     		unsigned long start;
369     		long size;
370     
371     		if ((minor & mask) == 0)
372     			break;
373     
374     		start = le32_to_cpu(p->start);
375     		size  = le32_to_cpu(p->size);
376     
377     		if (size < 0) {
378     			size = -size;
379     
380     			/*
381     			 * We use the first sector to identify what type
382     			 * this partition is...
383     			 */
384     			if (size > 1 && adfspart_check_ICSLinux(dev, start)) {
385     				start += 1;
386     				size -= 1;
387     			}
388     		}
389     
390     		if (size) {
391     			add_gd_partition(hd, minor, first_sector + start, size);
392     			minor++;
393     		}
394     	}
395     
396     	brelse(bh);
397     	return 1;
398     }
399     #endif
400     
401     #ifdef CONFIG_ACORN_PARTITION_POWERTEC
402     /*
403      * Purpose: allocate ICS partitions.
404      * Params : hd		- pointer to gendisk structure to store partition info.
405      *	    dev		- device number to access.
406      *	    first_sector- first readable sector on the device.
407      *	    minor	- first available minor on device.
408      * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok.
409      * Alloc  : hda  = whole drive
410      *	    hda1 = ADFS partition 0 on first drive.
411      *	    hda2 = ADFS partition 1 on first drive.
412      *		..etc..
413      */
414     static int
415     adfspart_check_POWERTEC(struct gendisk *hd, kdev_t dev,
416     			unsigned long first_sector, int minor)
417     {
418     	struct buffer_head *bh;
419     	struct ptec_partition *p;
420     	unsigned char checksum;
421     	int i;
422     
423     	if (!(bh = bread(dev, 0, 1024)))
424     		return -1;
425     
426     	for (checksum = 0x2a, i = 0; i < 511; i++)
427     		checksum += bh->b_data[i];
428     
429     	if (checksum != bh->b_data[511]) {
430     		bforget(bh);
431     		return 0;
432     	}
433     
434     	printk(" [POWERTEC]");
435     
436     	for (i = 0, p = (struct ptec_partition *)bh->b_data; i < 12; i++, p++) {
437     		unsigned long start;
438     		unsigned long size;
439     
440     		start = le32_to_cpu(p->start);
441     		size  = le32_to_cpu(p->size);
442     
443     		if (size)
444     			add_gd_partition(hd, minor, first_sector + start,
445     					 size);
446     		minor++;
447     	}
448     
449     	brelse(bh);
450     	return 1;
451     }
452     #endif
453     
454     static int (*partfn[])(struct gendisk *, kdev_t, unsigned long, int) = {
455     #ifdef CONFIG_ACORN_PARTITION_ICS
456     	adfspart_check_ICS,
457     #endif
458     #ifdef CONFIG_ACORN_PARTITION_CUMANA
459     	adfspart_check_CUMANA,
460     #endif
461     #ifdef CONFIG_ACORN_PARTITION_ADFS
462     	adfspart_check_ADFS,
463     #endif
464     #ifdef CONFIG_ACORN_PARTITION_POWERTEC
465     	adfspart_check_POWERTEC,
466     #endif
467     	NULL
468     };
469     /*
470      * Purpose: initialise all the partitions on an ADFS drive.
471      *          These may be other ADFS partitions or a Linux/RiscBSD/RISCiX
472      *	    partition.
473      *
474      * Params : hd		- pointer to gendisk structure
475      *          dev		- device number to access
476      *	    first_sect  - first available sector on the disk.
477      *	    first_minor	- first available minor on this device.
478      *
479      * Returns: -1 on error, 0 if not ADFS format, 1 if ok.
480      */
481     int acorn_partition(struct gendisk *hd, kdev_t dev,
482     		    unsigned long first_sect, int first_minor)
483     {
484     	int r = 0, i;
485     
486     	for (i = 0; partfn[i] && r == 0; i++)
487     		r = partfn[i](hd, dev, first_sect, first_minor);
488     
489     	if (r < 0 && warn_no_part)
490     		printk(" unable to read boot sectors / partition sectors\n");
491     	if (r > 0)
492     		printk("\n");
493     	return r;
494     }
495