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