From 77a5fe3cdfd19c17a00a281aee99b3c2fae95ebd Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jun 2003 20:47:06 -0700 Subject: [PATCH] [PATCH] Update Acorn partition parsing This patch: - re-enables cumana partition parsing - adds eesox partition parsing - makes the powertec partition parsing fail if sector 0 looks like a PC bios partition table Rather than having a single "acorn_partition" parser for all these types, we list them explicitly in check.c instead, along with some explaination about why they're where they are. --- fs/partitions/Kconfig | 12 ++- fs/partitions/acorn.c | 241 +++++++++++++++++++++++++++++++----------- fs/partitions/acorn.h | 59 ++--------- fs/partitions/check.c | 28 ++++- 4 files changed, 225 insertions(+), 115 deletions(-) diff --git a/fs/partitions/Kconfig b/fs/partitions/Kconfig index 5b51ac3c3a44..34e7225c303f 100644 --- a/fs/partitions/Kconfig +++ b/fs/partitions/Kconfig @@ -20,7 +20,17 @@ config ACORN_PARTITION help Support hard disks partitioned under Acorn operating systems. -# bool ' Cumana partition support' CONFIG_ACORN_PARTITION_CUMANA +config ACORN_PARTITION_CUMANA + bool "Cumana partition support" if PARTITION_ADVANCED && ACORN_PARTITION + default y if !PARTITION_ADVANCED && ARCH_ACORN + help + Say Y here if you would like to use hard disks under Linux which + were partitioned using the Cumana interface on Acorn machines. + +config ACORN_PARTITION_EESOX + bool "EESOX partition support" if PARTITION_ADVANCED && ACORN_PARTITION + default y if !PARTITION_ADVANCED && ARCH_ACORN + config ACORN_PARTITION_ICS bool "ICS partition support" if PARTITION_ADVANCED && ACORN_PARTITION default y if !PARTITION_ADVANCED && ARCH_ACORN diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c index 2516a2027b0e..5706a8893f31 100644 --- a/fs/partitions/acorn.c +++ b/fs/partitions/acorn.c @@ -7,14 +7,25 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Scan ADFS partitions on hard disk drives. + * Scan ADFS partitions on hard disk drives. Unfortunately, there + * isn't a standard for partitioning drives on Acorn machines, so + * every single manufacturer of SCSI and IDE cards created their own + * method. */ #include #include +#include #include "check.h" #include "acorn.h" +/* + * Partition types. (Oh for reusability) + */ +#define PARTITION_RISCIX_MFM 1 +#define PARTITION_RISCIX_SCSI 2 +#define PARTITION_LINUX 9 + static struct adfs_discrecord * adfs_partition(struct parsed_partitions *state, char *name, char *data, unsigned long first_sector, int slot) @@ -40,6 +51,21 @@ adfs_partition(struct parsed_partitions *state, char *name, char *data, } #ifdef CONFIG_ACORN_PARTITION_RISCIX + +struct riscix_part { + __u32 start; + __u32 length; + __u32 one; + char name[16]; +}; + +struct riscix_record { + __u32 magic; +#define RISCIX_MAGIC (0x4a657320) + __u32 date; + struct riscix_part part[8]; +}; + static int riscix_partition(struct parsed_partitions *state, struct block_device *bdev, unsigned long first_sect, int slot, unsigned long nr_sects) @@ -81,6 +107,15 @@ riscix_partition(struct parsed_partitions *state, struct block_device *bdev, } #endif +#define LINUX_NATIVE_MAGIC 0xdeafa1de +#define LINUX_SWAP_MAGIC 0xdeafab1e + +struct linux_part { + __u32 magic; + __u32 start_sect; + __u32 nr_sects; +}; + static int linux_partition(struct parsed_partitions *state, struct block_device *bdev, unsigned long first_sect, int slot, unsigned long nr_sects) @@ -114,7 +149,7 @@ linux_partition(struct parsed_partitions *state, struct block_device *bdev, } #ifdef CONFIG_ACORN_PARTITION_CUMANA -static int +int adfspart_check_CUMANA(struct parsed_partitions *state, struct block_device *bdev) { unsigned long first_sector = 0; @@ -126,7 +161,7 @@ adfspart_check_CUMANA(struct parsed_partitions *state, struct block_device *bdev int slot = 1; /* - * Try Cumana style partitions - sector 3 contains ADFS boot block + * Try Cumana style partitions - sector 6 contains ADFS boot block * with pointer to next 'drive'. * * There are unknowns in this code - is the 'cylinder number' of the @@ -206,7 +241,7 @@ adfspart_check_CUMANA(struct parsed_partitions *state, struct block_device *bdev * hda1 = ADFS partition on first drive. * hda2 = non-ADFS partition. */ -static int +int adfspart_check_ADFS(struct parsed_partitions *state, struct block_device *bdev) { unsigned long start_sect, nr_sects, sectscyl, heads; @@ -263,11 +298,18 @@ adfspart_check_ADFS(struct parsed_partitions *state, struct block_device *bdev) break; } } + printk("\n"); return 1; } #endif #ifdef CONFIG_ACORN_PARTITION_ICS + +struct ics_part { + __u32 start; + __s32 size; +}; + static int adfspart_check_ICSLinux(struct block_device *bdev, unsigned long block) { Sector sect; @@ -283,6 +325,22 @@ static int adfspart_check_ICSLinux(struct block_device *bdev, unsigned long bloc return result; } +/* + * Check for a valid ICS partition using the checksum. + */ +static inline int valid_ics_sector(const unsigned char *data) +{ + unsigned long sum; + int i; + + for (i = 0, sum = 0x50617274; i < 508; i++) + sum += data[i]; + + sum -= le32_to_cpu(*(__u32 *)(&data[508])); + + return sum == 0; +} + /* * Purpose: allocate ICS partitions. * Params : hd - pointer to gendisk structure to store partition info. @@ -293,15 +351,13 @@ static int adfspart_check_ICSLinux(struct block_device *bdev, unsigned long bloc * hda2 = ADFS partition 1 on first drive. * ..etc.. */ -static int +int adfspart_check_ICS(struct parsed_partitions *state, struct block_device *bdev) { - Sector sect; - unsigned char *data; - unsigned long sum; - unsigned int i; + const unsigned char *data; + const struct ics_part *p; int slot; - struct ics_part *p; + Sector sect; /* * Try ICS style partitions - sector 0 contains partition info. @@ -310,33 +366,33 @@ adfspart_check_ICS(struct parsed_partitions *state, struct block_device *bdev) if (!data) return -1; - /* - * check for a valid checksum - */ - for (i = 0, sum = 0x50617274; i < 508; i++) - sum += data[i]; - - sum -= le32_to_cpu(*(__u32 *)(&data[508])); - if (sum) { + if (!valid_ics_sector(data)) { put_dev_sector(sect); - return 0; /* not ICS partition table */ + return 0; } printk(" [ICS]"); - for (slot = 1, p = (struct ics_part *)data; p->size; p++) { + for (slot = 1, p = (const struct ics_part *)data; p->size; p++) { u32 start = le32_to_cpu(p->start); - u32 size = le32_to_cpu(p->size); + s32 size = le32_to_cpu(p->size); /* yes, it's signed. */ if (slot == state->limit) break; + /* + * Negative sizes tell the RISC OS ICS driver to ignore + * this partition - in effect it says that this does not + * contain an ADFS filesystem. + */ if (size < 0) { size = -size; /* - * We use the first sector to identify what type - * this partition is... + * Our own extension - We use the first sector + * of the partition to identify what type this + * partition is. We must not make this visible + * to the filesystem. */ if (size > 1 && adfspart_check_ICSLinux(bdev, start)) { start += 1; @@ -349,10 +405,39 @@ adfspart_check_ICS(struct parsed_partitions *state, struct block_device *bdev) } put_dev_sector(sect); + printk("\n"); return 1; } #endif +#ifdef CONFIG_ACORN_PARTITION_POWERTEC +struct ptec_part { + __u32 unused1; + __u32 unused2; + __u32 start; + __u32 size; + __u32 unused5; + char type[8]; +}; + +static inline int valid_ptec_sector(const unsigned char *data) +{ + unsigned char checksum = 0x2a; + int i; + + /* + * If it looks like a PC/BIOS partition, then it + * probably isn't PowerTec. + */ + if (data[510] == 0x55 && data[511] == 0xaa) + return 0; + + for (i = 0; i < 511; i++) + checksum += data[i]; + + return checksum == data[511]; +} + /* * Purpose: allocate ICS partitions. * Params : hd - pointer to gendisk structure to store partition info. @@ -363,14 +448,12 @@ adfspart_check_ICS(struct parsed_partitions *state, struct block_device *bdev) * hda2 = ADFS partition 1 on first drive. * ..etc.. */ -#ifdef CONFIG_ACORN_PARTITION_POWERTEC -static int +int adfspart_check_POWERTEC(struct parsed_partitions *state, struct block_device *bdev) { Sector sect; - unsigned char *data; - struct ptec_partition *p; - unsigned char checksum; + const unsigned char *data; + const struct ptec_part *p; int slot = 1; int i; @@ -378,17 +461,14 @@ adfspart_check_POWERTEC(struct parsed_partitions *state, struct block_device *bd if (!data) return -1; - for (checksum = 0x2a, i = 0; i < 511; i++) - checksum += data[i]; - - if (checksum != data[511]) { + if (!valid_ptec_sector(data)) { put_dev_sector(sect); return 0; } printk(" [POWERTEC]"); - for (i = 0, p = (struct ptec_partition *)data; i < 12; i++, p++) { + for (i = 0, p = (const struct ptec_part *)data; i < 12; i++, p++) { u32 start = le32_to_cpu(p->start); u32 size = le32_to_cpu(p->size); @@ -397,46 +477,81 @@ adfspart_check_POWERTEC(struct parsed_partitions *state, struct block_device *bd } put_dev_sector(sect); + printk("\n"); return 1; } #endif -static int (*partfn[])(struct parsed_partitions *, struct block_device *) = { -#ifdef CONFIG_ACORN_PARTITION_ICS - adfspart_check_ICS, -#endif -#ifdef CONFIG_ACORN_PARTITION_POWERTEC - adfspart_check_POWERTEC, -#endif -#ifdef CONFIG_ACORN_PARTITION_CUMANA - adfspart_check_CUMANA, -#endif -#ifdef CONFIG_ACORN_PARTITION_ADFS - adfspart_check_ADFS, -#endif - NULL +#ifdef CONFIG_ACORN_PARTITION_EESOX +struct eesox_part { + char magic[6]; + char name[10]; + u32 start; + u32 unused6; + u32 unused7; + u32 unused8; +}; + +/* + * Guess who created this format? + */ +static const char eesox_name[] = { + 'N', 'e', 'i', 'l', ' ', + 'C', 'r', 'i', 't', 'c', 'h', 'e', 'l', 'l', ' ', ' ' }; + /* - * Purpose: initialise all the partitions on an ADFS drive. - * These may be other ADFS partitions or a Linux/RiscBSD/RISCiX - * partition. + * EESOX SCSI partition format. * - * Params : hd - pointer to gendisk structure - * dev - device number to access + * This is a goddamned awful partition format. We don't seem to store + * the size of the partition in this table, only the start addresses. * - * Returns: -1 on error, 0 if not ADFS format, 1 if ok. + * There are two possibilities where the size comes from: + * 1. The individual ADFS boot block entries that are placed on the disk. + * 2. The start address of the next entry. */ -int acorn_partition(struct parsed_partitions *state, struct block_device *bdev) +int +adfspart_check_EESOX(struct parsed_partitions *state, struct block_device *bdev) { - int i; + Sector sect; + const unsigned char *data; + unsigned char buffer[256]; + struct eesox_part *p; + u32 start = 0; + int i, slot = 1; - for (i = 0; partfn[i]; i++) { - int r = partfn[i](state, bdev); - if (r) { - if (r > 0) - printk("\n"); - return r; - } + data = read_dev_sector(bdev, 7, §); + if (!data) + return -1; + + /* + * "Decrypt" the partition table. God knows why... + */ + for (i = 0; i < 256; i++) + buffer[i] = data[i] ^ eesox_name[i & 15]; + + put_dev_sector(sect); + + for (i = 0, p = (struct eesox_part *)buffer; i < 8; i++, p++) { + u32 next; + + if (memcmp(p->magic, "Eesox", 6)) + break; + + next = le32_to_cpu(p->start) + first_sector; + if (i) + put_partition(state, slot++, start, next - start); + start = next; } - return 0; + + if (i != 0) { + unsigned long size; + + size = hd->part[minor(to_kdev_t(bdev->bd_dev))].nr_sects; + add_gd_partition(hd, minor++, start, size - start); + printk("\n"); + } + + return i ? 1 : 0; } +#endif diff --git a/fs/partitions/acorn.h b/fs/partitions/acorn.h index 7c984234ae81..81fd50ecc080 100644 --- a/fs/partitions/acorn.h +++ b/fs/partitions/acorn.h @@ -1,53 +1,14 @@ /* - * fs/partitions/acorn.h + * linux/fs/partitions/acorn.h * - * Copyright (C) 1996-1998 Russell King - */ -#include - -/* - * Partition types. (Oh for reusability) + * Copyright (C) 1996-2001 Russell King. + * + * I _hate_ this partitioning mess - why can't we have one defined + * format, and everyone stick to it? */ -#define PARTITION_RISCIX_MFM 1 -#define PARTITION_RISCIX_SCSI 2 -#define PARTITION_LINUX 9 - -struct riscix_part { - __u32 start; - __u32 length; - __u32 one; - char name[16]; -}; - -struct riscix_record { - __u32 magic; -#define RISCIX_MAGIC (0x4a657320) - __u32 date; - struct riscix_part part[8]; -}; - -#define LINUX_NATIVE_MAGIC 0xdeafa1de -#define LINUX_SWAP_MAGIC 0xdeafab1e - -struct linux_part { - __u32 magic; - __u32 start_sect; - __u32 nr_sects; -}; - -struct ics_part { - __u32 start; - __s32 size; -}; - -struct ptec_partition { - __u32 unused1; - __u32 unused2; - __u32 start; - __u32 size; - __u32 unused5; - char type[8]; -}; - -int acorn_partition(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_CUMANA(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_ADFS(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_ICS(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_POWERTEC(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_EESOX(struct parsed_partitions *state, struct block_device *bdev); diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 8f09ea0494f8..fa9d8eb03f98 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -45,9 +45,33 @@ extern void md_autodetect_dev(dev_t dev); int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ static int (*check_part[])(struct parsed_partitions *, struct block_device *) = { -#ifdef CONFIG_ACORN_PARTITION - acorn_partition, + /* + * Probe partition formats with tables at disk address 0 + * that also have an ADFS boot block at 0xdc0. + */ +#ifdef CONFIG_ACORN_PARTITION_ICS + adfspart_check_ICS, #endif +#ifdef CONFIG_ACORN_PARTITION_POWERTEC + adfspart_check_POWERTEC, +#endif +#ifdef CONFIG_ACORN_PARTITION_EESOX + adfspart_check_EESOX, +#endif + + /* + * Now move on to formats that only have partition info at + * disk address 0xdc0. Since these may also have stale + * PC/BIOS partition tables, they need to come before + * the msdos entry. + */ +#ifdef CONFIG_ACORN_PARTITION_CUMANA + adfspart_check_CUMANA, +#endif +#ifdef CONFIG_ACORN_PARTITION_ADFS + adfspart_check_ADFS, +#endif + #ifdef CONFIG_EFI_PARTITION efi_partition, /* this must come before msdos */ #endif -- 2.39.5