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