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