linux/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.  Unfortunately, there
  11 *  isn't a standard for partitioning drives on Acorn machines, so
  12 *  every single manufacturer of SCSI and IDE cards created their own
  13 *  method.
  14 */
  15#include <linux/buffer_head.h>
  16#include <linux/adfs_fs.h>
  17
  18#include "check.h"
  19#include "acorn.h"
  20
  21/*
  22 * Partition types. (Oh for reusability)
  23 */
  24#define PARTITION_RISCIX_MFM    1
  25#define PARTITION_RISCIX_SCSI   2
  26#define PARTITION_LINUX         9
  27
  28#if defined(CONFIG_ACORN_PARTITION_CUMANA) || \
  29        defined(CONFIG_ACORN_PARTITION_ADFS)
  30static struct adfs_discrecord *
  31adfs_partition(struct parsed_partitions *state, char *name, char *data,
  32               unsigned long first_sector, int slot)
  33{
  34        struct adfs_discrecord *dr;
  35        unsigned int nr_sects;
  36
  37        if (adfs_checkbblk(data))
  38                return NULL;
  39
  40        dr = (struct adfs_discrecord *)(data + 0x1c0);
  41
  42        if (dr->disc_size == 0 && dr->disc_size_high == 0)
  43                return NULL;
  44
  45        nr_sects = (le32_to_cpu(dr->disc_size_high) << 23) |
  46                   (le32_to_cpu(dr->disc_size) >> 9);
  47
  48        if (name)
  49                printk(" [%s]", name);
  50        put_partition(state, slot, first_sector, nr_sects);
  51        return dr;
  52}
  53#endif
  54
  55#ifdef CONFIG_ACORN_PARTITION_RISCIX
  56
  57struct riscix_part {
  58        __le32  start;
  59        __le32  length;
  60        __le32  one;
  61        char    name[16];
  62};
  63
  64struct riscix_record {
  65        __le32  magic;
  66#define RISCIX_MAGIC    cpu_to_le32(0x4a657320)
  67        __le32  date;
  68        struct riscix_part part[8];
  69};
  70
  71#if defined(CONFIG_ACORN_PARTITION_CUMANA) || \
  72        defined(CONFIG_ACORN_PARTITION_ADFS)
  73static int
  74riscix_partition(struct parsed_partitions *state, struct block_device *bdev,
  75                unsigned long first_sect, int slot, unsigned long nr_sects)
  76{
  77        Sector sect;
  78        struct riscix_record *rr;
  79        
  80        rr = (struct riscix_record *)read_dev_sector(bdev, first_sect, &sect);
  81        if (!rr)
  82                return -1;
  83
  84        printk(" [RISCiX]");
  85
  86
  87        if (rr->magic == RISCIX_MAGIC) {
  88                unsigned long size = nr_sects > 2 ? 2 : nr_sects;
  89                int part;
  90
  91                printk(" <");
  92
  93                put_partition(state, slot++, first_sect, size);
  94                for (part = 0; part < 8; part++) {
  95                        if (rr->part[part].one &&
  96                            memcmp(rr->part[part].name, "All\0", 4)) {
  97                                put_partition(state, slot++,
  98                                        le32_to_cpu(rr->part[part].start),
  99                                        le32_to_cpu(rr->part[part].length));
 100                                printk("(%s)", rr->part[part].name);
 101                        }
 102                }
 103
 104                printk(" >\n");
 105        } else {
 106                put_partition(state, slot++, first_sect, nr_sects);
 107        }
 108
 109        put_dev_sector(sect);
 110        return slot;
 111}
 112#endif
 113#endif
 114
 115#define LINUX_NATIVE_MAGIC 0xdeafa1de
 116#define LINUX_SWAP_MAGIC   0xdeafab1e
 117
 118struct linux_part {
 119        __le32 magic;
 120        __le32 start_sect;
 121        __le32 nr_sects;
 122};
 123
 124#if defined(CONFIG_ACORN_PARTITION_CUMANA) || \
 125        defined(CONFIG_ACORN_PARTITION_ADFS)
 126static int
 127linux_partition(struct parsed_partitions *state, struct block_device *bdev,
 128                unsigned long first_sect, int slot, unsigned long nr_sects)
 129{
 130        Sector sect;
 131        struct linux_part *linuxp;
 132        unsigned long size = nr_sects > 2 ? 2 : nr_sects;
 133
 134        printk(" [Linux]");
 135
 136        put_partition(state, slot++, first_sect, size);
 137
 138        linuxp = (struct linux_part *)read_dev_sector(bdev, first_sect, &sect);
 139        if (!linuxp)
 140                return -1;
 141
 142        printk(" <");
 143        while (linuxp->magic == cpu_to_le32(LINUX_NATIVE_MAGIC) ||
 144               linuxp->magic == cpu_to_le32(LINUX_SWAP_MAGIC)) {
 145                if (slot == state->limit)
 146                        break;
 147                put_partition(state, slot++, first_sect +
 148                                 le32_to_cpu(linuxp->start_sect),
 149                                 le32_to_cpu(linuxp->nr_sects));
 150                linuxp ++;
 151        }
 152        printk(" >");
 153
 154        put_dev_sector(sect);
 155        return slot;
 156}
 157#endif
 158
 159#ifdef CONFIG_ACORN_PARTITION_CUMANA
 160int
 161adfspart_check_CUMANA(struct parsed_partitions *state, struct block_device *bdev)
 162{
 163        unsigned long first_sector = 0;
 164        unsigned int start_blk = 0;
 165        Sector sect;
 166        unsigned char *data;
 167        char *name = "CUMANA/ADFS";
 168        int first = 1;
 169        int slot = 1;
 170
 171        /*
 172         * Try Cumana style partitions - sector 6 contains ADFS boot block
 173         * with pointer to next 'drive'.
 174         *
 175         * There are unknowns in this code - is the 'cylinder number' of the
 176         * next partition relative to the start of this one - I'm assuming
 177         * it is.
 178         *
 179         * Also, which ID did Cumana use?
 180         *
 181         * This is totally unfinished, and will require more work to get it
 182         * going. Hence it is totally untested.
 183         */
 184        do {
 185                struct adfs_discrecord *dr;
 186                unsigned int nr_sects;
 187
 188                data = read_dev_sector(bdev, start_blk * 2 + 6, &sect);
 189                if (!data)
 190                        return -1;
 191
 192                if (slot == state->limit)
 193                        break;
 194
 195                dr = adfs_partition(state, name, data, first_sector, slot++);
 196                if (!dr)
 197                        break;
 198
 199                name = NULL;
 200
 201                nr_sects = (data[0x1fd] + (data[0x1fe] << 8)) *
 202                           (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) *
 203                           dr->secspertrack;
 204
 205                if (!nr_sects)
 206                        break;
 207
 208                first = 0;
 209                first_sector += nr_sects;
 210                start_blk += nr_sects >> (BLOCK_SIZE_BITS - 9);
 211                nr_sects = 0; /* hmm - should be partition size */
 212
 213                switch (data[0x1fc] & 15) {
 214                case 0: /* No partition / ADFS? */
 215                        break;
 216
 217#ifdef CONFIG_ACORN_PARTITION_RISCIX
 218                case PARTITION_RISCIX_SCSI:
 219                        /* RISCiX - we don't know how to find the next one. */
 220                        slot = riscix_partition(state, bdev, first_sector,
 221                                                 slot, nr_sects);
 222                        break;
 223#endif
 224
 225                case PARTITION_LINUX:
 226                        slot = linux_partition(state, bdev, first_sector,
 227                                                slot, nr_sects);
 228                        break;
 229                }
 230                put_dev_sector(sect);
 231                if (slot == -1)
 232                        return -1;
 233        } while (1);
 234        put_dev_sector(sect);
 235        return first ? 0 : 1;
 236}
 237#endif
 238
 239#ifdef CONFIG_ACORN_PARTITION_ADFS
 240/*
 241 * Purpose: allocate ADFS partitions.
 242 *
 243 * Params : hd          - pointer to gendisk structure to store partition info.
 244 *          dev         - device number to access.
 245 *
 246 * Returns: -1 on error, 0 for no ADFS boot sector, 1 for ok.
 247 *
 248 * Alloc  : hda  = whole drive
 249 *          hda1 = ADFS partition on first drive.
 250 *          hda2 = non-ADFS partition.
 251 */
 252int
 253adfspart_check_ADFS(struct parsed_partitions *state, struct block_device *bdev)
 254{
 255        unsigned long start_sect, nr_sects, sectscyl, heads;
 256        Sector sect;
 257        unsigned char *data;
 258        struct adfs_discrecord *dr;
 259        unsigned char id;
 260        int slot = 1;
 261
 262        data = read_dev_sector(bdev, 6, &sect);
 263        if (!data)
 264                return -1;
 265
 266        dr = adfs_partition(state, "ADFS", data, 0, slot++);
 267        if (!dr) {
 268                put_dev_sector(sect);
 269                return 0;
 270        }
 271
 272        heads = dr->heads + ((dr->lowsector >> 6) & 1);
 273        sectscyl = dr->secspertrack * heads;
 274        start_sect = ((data[0x1fe] << 8) + data[0x1fd]) * sectscyl;
 275        id = data[0x1fc] & 15;
 276        put_dev_sector(sect);
 277
 278        /*
 279         * Work out start of non-adfs partition.
 280         */
 281        nr_sects = (bdev->bd_inode->i_size >> 9) - start_sect;
 282
 283        if (start_sect) {
 284                switch (id) {
 285#ifdef CONFIG_ACORN_PARTITION_RISCIX
 286                case PARTITION_RISCIX_SCSI:
 287                case PARTITION_RISCIX_MFM:
 288                        slot = riscix_partition(state, bdev, start_sect,
 289                                                 slot, nr_sects);
 290                        break;
 291#endif
 292
 293                case PARTITION_LINUX:
 294                        slot = linux_partition(state, bdev, start_sect,
 295                                                slot, nr_sects);
 296                        break;
 297                }
 298        }
 299        printk("\n");
 300        return 1;
 301}
 302#endif
 303
 304#ifdef CONFIG_ACORN_PARTITION_ICS
 305
 306struct ics_part {
 307        __le32 start;
 308        __le32 size;
 309};
 310
 311static int adfspart_check_ICSLinux(struct block_device *bdev, unsigned long block)
 312{
 313        Sector sect;
 314        unsigned char *data = read_dev_sector(bdev, block, &sect);
 315        int result = 0;
 316
 317        if (data) {
 318                if (memcmp(data, "LinuxPart", 9) == 0)
 319                        result = 1;
 320                put_dev_sector(sect);
 321        }
 322
 323        return result;
 324}
 325
 326/*
 327 * Check for a valid ICS partition using the checksum.
 328 */
 329static inline int valid_ics_sector(const unsigned char *data)
 330{
 331        unsigned long sum;
 332        int i;
 333
 334        for (i = 0, sum = 0x50617274; i < 508; i++)
 335                sum += data[i];
 336
 337        sum -= le32_to_cpu(*(__le32 *)(&data[508]));
 338
 339        return sum == 0;
 340}
 341
 342/*
 343 * Purpose: allocate ICS partitions.
 344 * Params : hd          - pointer to gendisk structure to store partition info.
 345 *          dev         - device number to access.
 346 * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok.
 347 * Alloc  : hda  = whole drive
 348 *          hda1 = ADFS partition 0 on first drive.
 349 *          hda2 = ADFS partition 1 on first drive.
 350 *              ..etc..
 351 */
 352int
 353adfspart_check_ICS(struct parsed_partitions *state, struct block_device *bdev)
 354{
 355        const unsigned char *data;
 356        const struct ics_part *p;
 357        int slot;
 358        Sector sect;
 359
 360        /*
 361         * Try ICS style partitions - sector 0 contains partition info.
 362         */
 363        data = read_dev_sector(bdev, 0, &sect);
 364        if (!data)
 365                return -1;
 366
 367        if (!valid_ics_sector(data)) {
 368                put_dev_sector(sect);
 369                return 0;
 370        }
 371
 372        printk(" [ICS]");
 373
 374        for (slot = 1, p = (const struct ics_part *)data; p->size; p++) {
 375                u32 start = le32_to_cpu(p->start);
 376                s32 size = le32_to_cpu(p->size); /* yes, it's signed. */
 377
 378                if (slot == state->limit)
 379                        break;
 380
 381                /*
 382                 * Negative sizes tell the RISC OS ICS driver to ignore
 383                 * this partition - in effect it says that this does not
 384                 * contain an ADFS filesystem.
 385                 */
 386                if (size < 0) {
 387                        size = -size;
 388
 389                        /*
 390                         * Our own extension - We use the first sector
 391                         * of the partition to identify what type this
 392                         * partition is.  We must not make this visible
 393                         * to the filesystem.
 394                         */
 395                        if (size > 1 && adfspart_check_ICSLinux(bdev, start)) {
 396                                start += 1;
 397                                size -= 1;
 398                        }
 399                }
 400
 401                if (size)
 402                        put_partition(state, slot++, start, size);
 403        }
 404
 405        put_dev_sector(sect);
 406        printk("\n");
 407        return 1;
 408}
 409#endif
 410
 411#ifdef CONFIG_ACORN_PARTITION_POWERTEC
 412struct ptec_part {
 413        __le32 unused1;
 414        __le32 unused2;
 415        __le32 start;
 416        __le32 size;
 417        __le32 unused5;
 418        char type[8];
 419};
 420
 421static inline int valid_ptec_sector(const unsigned char *data)
 422{
 423        unsigned char checksum = 0x2a;
 424        int i;
 425
 426        /*
 427         * If it looks like a PC/BIOS partition, then it
 428         * probably isn't PowerTec.
 429         */
 430        if (data[510] == 0x55 && data[511] == 0xaa)
 431                return 0;
 432
 433        for (i = 0; i < 511; i++)
 434                checksum += data[i];
 435
 436        return checksum == data[511];
 437}
 438
 439/*
 440 * Purpose: allocate ICS partitions.
 441 * Params : hd          - pointer to gendisk structure to store partition info.
 442 *          dev         - device number to access.
 443 * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok.
 444 * Alloc  : hda  = whole drive
 445 *          hda1 = ADFS partition 0 on first drive.
 446 *          hda2 = ADFS partition 1 on first drive.
 447 *              ..etc..
 448 */
 449int
 450adfspart_check_POWERTEC(struct parsed_partitions *state, struct block_device *bdev)
 451{
 452        Sector sect;
 453        const unsigned char *data;
 454        const struct ptec_part *p;
 455        int slot = 1;
 456        int i;
 457
 458        data = read_dev_sector(bdev, 0, &sect);
 459        if (!data)
 460                return -1;
 461
 462        if (!valid_ptec_sector(data)) {
 463                put_dev_sector(sect);
 464                return 0;
 465        }
 466
 467        printk(" [POWERTEC]");
 468
 469        for (i = 0, p = (const struct ptec_part *)data; i < 12; i++, p++) {
 470                u32 start = le32_to_cpu(p->start);
 471                u32 size = le32_to_cpu(p->size);
 472
 473                if (size)
 474                        put_partition(state, slot++, start, size);
 475        }
 476
 477        put_dev_sector(sect);
 478        printk("\n");
 479        return 1;
 480}
 481#endif
 482
 483#ifdef CONFIG_ACORN_PARTITION_EESOX
 484struct eesox_part {
 485        char    magic[6];
 486        char    name[10];
 487        __le32  start;
 488        __le32  unused6;
 489        __le32  unused7;
 490        __le32  unused8;
 491};
 492
 493/*
 494 * Guess who created this format?
 495 */
 496static const char eesox_name[] = {
 497        'N', 'e', 'i', 'l', ' ',
 498        'C', 'r', 'i', 't', 'c', 'h', 'e', 'l', 'l', ' ', ' '
 499};
 500
 501/*
 502 * EESOX SCSI partition format.
 503 *
 504 * This is a goddamned awful partition format.  We don't seem to store
 505 * the size of the partition in this table, only the start addresses.
 506 *
 507 * There are two possibilities where the size comes from:
 508 *  1. The individual ADFS boot block entries that are placed on the disk.
 509 *  2. The start address of the next entry.
 510 */
 511int
 512adfspart_check_EESOX(struct parsed_partitions *state, struct block_device *bdev)
 513{
 514        Sector sect;
 515        const unsigned char *data;
 516        unsigned char buffer[256];
 517        struct eesox_part *p;
 518        sector_t start = 0;
 519        int i, slot = 1;
 520
 521        data = read_dev_sector(bdev, 7, &sect);
 522        if (!data)
 523                return -1;
 524
 525        /*
 526         * "Decrypt" the partition table.  God knows why...
 527         */
 528        for (i = 0; i < 256; i++)
 529                buffer[i] = data[i] ^ eesox_name[i & 15];
 530
 531        put_dev_sector(sect);
 532
 533        for (i = 0, p = (struct eesox_part *)buffer; i < 8; i++, p++) {
 534                sector_t next;
 535
 536                if (memcmp(p->magic, "Eesox", 6))
 537                        break;
 538
 539                next = le32_to_cpu(p->start);
 540                if (i)
 541                        put_partition(state, slot++, start, next - start);
 542                start = next;
 543        }
 544
 545        if (i != 0) {
 546                sector_t size;
 547
 548                size = get_capacity(bdev->bd_disk);
 549                put_partition(state, slot++, start, size - start);
 550                printk("\n");
 551        }
 552
 553        return i ? 1 : 0;
 554}
 555#endif
 556
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.