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