551 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 1996-2000 Russell King.4 *5 * Scan ADFS partitions on hard disk drives. Unfortunately, there6 * isn't a standard for partitioning drives on Acorn machines, so7 * every single manufacturer of SCSI and IDE cards created their own8 * 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 119#define PARTITION_RISCIX_SCSI 220#define PARTITION_LINUX 921 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#endif51 52#ifdef CONFIG_ACORN_PARTITION_RISCIX53 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, §);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#endif112#endif113 114#define LINUX_NATIVE_MAGIC 0xdeafa1de115#define LINUX_SWAP_MAGIC 0xdeafab1e116 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, §);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#endif157 158#ifdef CONFIG_ACORN_PARTITION_CUMANA159int 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 block171 * with pointer to next 'drive'.172 *173 * There are unknowns in this code - is the 'cylinder number' of the174 * next partition relative to the start of this one - I'm assuming175 * it is.176 *177 * Also, which ID did Cumana use?178 *179 * This is totally unfinished, and will require more work to get it180 * 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, §);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_RISCIX216 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#endif222 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#endif236 237#ifdef CONFIG_ACORN_PARTITION_ADFS238/*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 drive247 * 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, §);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_RISCIX283 case PARTITION_RISCIX_SCSI:284 case PARTITION_RISCIX_MFM:285 riscix_partition(state, start_sect, slot,286 nr_sects);287 break;288#endif289 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#endif300 301#ifdef CONFIG_ACORN_PARTITION_ICS302 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, §);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 drive346 * 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, §);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 ignore380 * this partition - in effect it says that this does not381 * contain an ADFS filesystem.382 */383 if (size < 0) {384 size = -size;385 386 /*387 * Our own extension - We use the first sector388 * of the partition to identify what type this389 * partition is. We must not make this visible390 * 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#endif407 408#ifdef CONFIG_ACORN_PARTITION_POWERTEC409struct 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 it425 * 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 drive442 * 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, §);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#endif478 479#ifdef CONFIG_ACORN_PARTITION_EESOX480struct 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 store501 * 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, §);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#endif551