[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: GPT partlabel support for Mach



Hi,

On Sat, May 30, 2020 at 02:37:06PM +0200, Samuel Thibault wrote:
> Would you assign copyright to the FSF so we can commit the support in
> upstream gnumach?
Sorry for the delay, but the FSF page made me seethe a little (a lot),
and that's not a good state to be writing mails in.

I cannot fill out the FSF's form for reasons too broad to fit on this
margin (i.a. I don't have a "full legal name" that can be expressed
"in ASCII characters", nor do I do wish to give random american
corporations my personal details, especially since I don't see a GDPR
notice anywhere).

However, except for the core of ether_crc_le_hole(), based on code
already present in GNU Mach, I wrote this entirely on my own,
and no other entities have a basis to claim this code; therefore,
if that works for you, I am relinquishing any right to copying of this
patch, which means that it is yours to claim and similarly
relinquish/assign to the FSF, if the foundation doesn't accept code
from the public domain.

> Please avoid assignation inside function parameters, that's difficult to
> read.
Updated.

Thanks in advance for understanding,
наб

-- >8 --
Date: Thu, 28 May 2020 03:22:30 +0200
Subject: [PATCH v2] Support GPT disklabels and build them by default

Based on UEFI 2.8A spec
---
 i386/linux/dev/include/linux/autoconf.h |   2 +
 linux/dev/drivers/block/genhd.c         | 251 ++++++++++++++++++++++++
 linux/dev/include/linux/genhd.h         |  67 +++++++
 3 files changed, 320 insertions(+)

diff --git a/i386/linux/dev/include/linux/autoconf.h b/i386/linux/dev/include/linux/autoconf.h
index 75ff2aa..bd035d4 100644
--- a/i386/linux/dev/include/linux/autoconf.h
+++ b/i386/linux/dev/include/linux/autoconf.h
@@ -246,6 +246,8 @@
 #undef	CONFIG_BSD_DISKLABEL
 #undef	CONFIG_SMD_DISKLABEL
 
+#define CONFIG_GPT_DISKLABEL 1
+
 /*
  * Character devices
  */
diff --git a/linux/dev/drivers/block/genhd.c b/linux/dev/drivers/block/genhd.c
index f38654b..23c1e12 100644
--- a/linux/dev/drivers/block/genhd.c
+++ b/linux/dev/drivers/block/genhd.c
@@ -29,6 +29,11 @@
 #endif
 #include <linux/hdreg.h>
 #include <alloca.h>
+#ifdef CONFIG_GPT_DISKLABEL
+#include <linux/blkdev.h>
+#include <kern/kalloc.h>
+#include <stddef.h>
+#endif
 
 #include <asm/system.h>
 
@@ -276,6 +281,246 @@ static void bsd_disklabel_partition(struct gendisk *hd, kdev_t dev)
 }
 #endif
 
+#ifdef CONFIG_GPT_DISKLABEL
+/*
+ * Compute a CRC32 but treat some range as if it were zeros.
+ *
+ * Straight copy of ether_crc_le() from linux/pcmcia-cs/include/linux/crc32.h, except for the first if/else
+ */
+static inline unsigned ether_crc_le_hole(int length, unsigned char *data, unsigned int skip_offset, unsigned int skip_length)
+{
+	static unsigned const ethernet_polynomial_le = 0xedb88320U;
+	unsigned int crc = 0xffffffff;      /* Initial value. */
+	while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		if(skip_offset == 0 && skip_length-- != 0)
+			current_octet = 0;
+		else
+			--skip_offset;
+		int bit;
+		for (bit = 8; --bit >= 0; current_octet >>= 1) {
+			if ((crc ^ current_octet) & 1) {
+				crc >>= 1;
+				crc ^= ethernet_polynomial_le;
+			} else
+				crc >>= 1;
+		}
+	}
+	return crc;
+}
+
+/*
+ * Read in a full GPT array into a contiguous chunk, allocates *PP_S bytes into *PP.
+ *
+ * An attempt to do as few round-trips as possible is made by reading a PAGE_SIZE at a time,
+ * since that's the bread() maximum.
+ */
+static int gpt_read_part_table(void **pp, vm_size_t *pp_s, kdev_t dev, int bsize, __u64 first_sector, struct gpt_disklabel_header *h)
+{
+	__u64 lba = first_sector + h->h_part_table_lba;
+	__u32 bytes_left = *pp_s = h->h_part_table_len * h->h_part_table_entry_size;
+	struct buffer_head *bh;
+	void *cur = *pp = (void *)kalloc(*pp_s);
+	if (!cur) {
+		printk(" unable to allocate GPT partition table buffer");
+		return -2;
+	}
+
+	while (bytes_left) {
+		unsigned bytes_to_read = MIN(bytes_left, PAGE_SIZE);
+		if(!(bh = bread(dev, lba, bytes_to_read))) {
+			printk(" unable to read partition table array");
+			return -3;
+		}
+
+		memcpy(cur, bh->b_data, bytes_to_read);
+		cur += bytes_to_read;
+		bytes_left -= bytes_to_read;
+		lba += PAGE_SIZE / bsize;
+
+		brelse(bh);
+	}
+
+	return 0;
+}
+
+/*
+ * Sequence from section 5.3.2 of spec 2.8A:
+ * signature, CRC, lba_current matches, partition table CRC, primary: check backup for validity
+ */
+static int gpt_verify_header(void **pp, vm_size_t *pp_s, kdev_t dev, int bsize, __u64 first_sector, __u64 lba, struct gpt_disklabel_header *h)
+{
+	int res;
+	__u32 crc;
+
+	if (memcmp(h->h_signature, GPT_SIGNATURE, strlen(GPT_SIGNATURE)) != 0) {
+		printk(" bad GPT signature \"%c%c%c%c%c%c%c%c\";",
+			h->h_signature[0], h->h_signature[1], h->h_signature[2], h->h_signature[3],
+			h->h_signature[4], h->h_signature[5], h->h_signature[6], h->h_signature[7]);
+		return 1;
+	}
+
+	crc = ether_crc_le_hole(h->h_header_size, (void *)h,
+		offsetof(struct gpt_disklabel_header, h_header_crc), sizeof(h->h_header_crc)) ^ ~0;
+	if (crc != h->h_header_crc) {
+		printk(" bad header CRC: %x != %x;", crc, h->h_header_crc);
+		return 2;
+	}
+
+	if (h->h_lba_current != lba) {
+		printk(" current LBA mismatch: %lld != %lld;", h->h_lba_current, lba);
+		return 3;
+	}
+
+	if (*pp) {
+		kfree((vm_offset_t)*pp, *pp_s);
+		*pp = NULL;
+	}
+	if ((res = gpt_read_part_table(pp, pp_s, dev, bsize, first_sector, h)))
+		return res;
+
+	crc = ether_crc_le_hole(*pp_s, *pp, 0, 0) ^ ~0;
+	if (crc != h->h_part_table_crc) {
+		printk(" bad partition table CRC: %x != %x;", crc, h->h_part_table_crc);
+		return 4;
+	}
+
+	for (int i = h->h_header_size; i < bsize; ++i)
+		res |= ((char*)h)[i];
+	if (res) {
+		printk(" rest of GPT block dirty;");
+		return 5;
+	}
+
+	return 0;
+}
+
+static void gpt_print_part_name(struct gpt_disklabel_part *p)
+{
+	for(int n = 0; n < sizeof(p->p_name) / sizeof(*p->p_name) && p->p_name[n]; ++n)
+		if(p->p_name[n] & ~0xFF)
+			printk("?");	/* Can't support all of Unicode, but don't print garbage at least... */
+		else
+			printk("%c", p->p_name[n]);
+}
+
+#ifdef DEBUG
+static void gpt_print_guid(struct gpt_guid *guid)
+{
+	printk("%08X-%04X-%04X-%02X%02X-", guid->g_time_low, guid->g_time_mid, guid->g_time_high_version, guid->g_clock_sec_high, guid->g_clock_sec_low);
+	for (int i = 0; i < sizeof(guid->g_node_id); ++i)
+		printk("%02X", guid->g_node_id[i]);
+}
+
+static void gpt_dump_header(struct gpt_disklabel_header *h)
+{
+	printk(" [h_signature: \"%c%c%c%c%c%c%c%c\"; ",
+		h->h_signature[0], h->h_signature[1], h->h_signature[2], h->h_signature[3],
+		h->h_signature[4], h->h_signature[5], h->h_signature[6], h->h_signature[7]);
+	printk("h_revision: %x; ", h->h_revision);
+	printk("h_header_size: %u; ", h->h_header_size);
+	printk("h_header_crc: %x; ", h->h_header_crc);
+	printk("h_reserved: %u; ", h->h_reserved);
+	printk("h_lba_current: %llu; ", h->h_lba_current);
+	printk("h_lba_backup: %llu; ", h->h_lba_backup);
+	printk("h_lba_usable_first: %llu; ", h->h_lba_usable_first);
+	printk("h_lba_usable_last: %llu; ", h->h_lba_usable_last);
+	printk("h_guid: "); gpt_print_guid(&h->h_guid); printk("; ");
+	printk("h_part_table_lba: %llu; ", h->h_part_table_lba);
+	printk("h_part_table_len: %u; ", h->h_part_table_len);
+	printk("h_part_table_crc: %x]", h->h_part_table_crc);
+}
+
+static void gpt_dump_part(struct gpt_disklabel_part *p, int i)
+{
+	printk(" part#%d:[", i);
+	printk("p_type: "); gpt_print_guid(&p->p_type);
+	printk("; p_guid:"); gpt_print_guid(&p->p_guid);
+	printk("; p_lba_first: %llu", p->p_lba_first);
+	printk("; p_lba_last: %llu", p->p_lba_last);
+	printk("; p_attrs: %llx", p->p_attrs);
+	printk("; p_name: \""); gpt_print_part_name(p); printk("\"]");
+}
+#else
+static void gpt_dump_header(struct gpt_disklabel_header *h) {}
+static void gpt_dump_part(struct gpt_disklabel_part *p, int i) {}
+#endif
+
+static int gpt_partition(struct gendisk *hd, kdev_t dev, __u64 first_sector, int minor)
+{
+	struct buffer_head *bh;
+	struct gpt_disklabel_header *h;
+	void *pp = NULL; vm_size_t pp_s = 0;
+	int res, bsize = 512;
+	/* Note: this must be set by the driver; SCSI does --
+	 *       only, in practice, it always sets this to 512, see sd_init() in sd.c */
+	if (hardsect_size[MAJOR(dev)] && hardsect_size[MAJOR(dev)][MINOR(dev)])
+		bsize = hardsect_size[MAJOR(dev)][MINOR(dev)];
+	set_blocksize(dev,bsize);	/* Must override read block size since GPT has pointers, stolen from amiga_partition(). */
+	if (!(bh = bread(dev, first_sector + 1, bsize))) {
+		printk("unable to read GPT");
+		res = -1;
+		goto done;
+	}
+
+	h = (struct gpt_disklabel_header *)bh->b_data;
+	gpt_dump_header(h);
+
+	res = gpt_verify_header(&pp, &pp_s, dev, bsize, first_sector, 1, h);
+	if (res < 0)
+		goto done;
+	else if (res > 0) {
+		printk(" main GPT dirty, trying backup at %llu;", h->h_lba_backup);
+		__u64 lba = h->h_lba_backup;
+		brelse(bh);
+
+		if (!(bh = bread(dev, first_sector + lba, bsize))) {
+			printk("unable to read backup GPT");
+			res = -4;
+			goto done;
+		}
+
+		h = (struct gpt_disklabel_header *)bh->b_data;
+		gpt_dump_header(h);
+
+		res = gpt_verify_header(&pp, &pp_s, dev, bsize, first_sector, lba, h);
+		if (res < 0)
+			goto done;
+		else if (res > 0) {
+			printk(" backup GPT dirty as well; cowardly refusing to continue");
+			res = -5;
+			goto done;
+		}
+	}
+
+	/* At least one good GPT+array */
+
+	for(int i = 0; i < h->h_part_table_len; ++i, ++minor) {
+		struct gpt_disklabel_part *p =
+			(struct gpt_disklabel_part *) (pp + i * h->h_part_table_entry_size);
+		if(memcmp(&p->p_type, &GPT_GUID_TYPE_UNUSED, sizeof(struct gpt_guid)) == 0)
+			continue;
+		gpt_dump_part(p, i);
+
+		if (minor > hd->max_nr * hd->max_p) {
+			printk(" [ignoring GPT partition %d \"", i); gpt_print_part_name(p); printk("\": too many partitions (max %d)]", hd->max_p);
+		} else {
+			add_partition(hd, minor, first_sector + p->p_lba_first, p->p_lba_last - p->p_lba_first + 1);
+			if(p->p_name[0]) {
+				printk(" ("); gpt_print_part_name(p); printk(")");
+			}
+		}
+	}
+
+done:
+	brelse(bh);
+	set_blocksize(dev,BLOCK_SIZE);
+	kfree((vm_offset_t)pp, pp_s);
+	printk("\n");
+	return !res;
+}
+#endif
+
 static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
 {
 	int i, minor = current_minor;
@@ -381,6 +626,12 @@ check_table:
 	for (i=1 ; i<=4 ; minor++,i++,p++) {
 		if (!NR_SECTS(p))
 			continue;
+#ifdef CONFIG_GPT_DISKLABEL
+		if (SYS_IND(p) == GPT_PARTITION) {
+			brelse(bh);
+			return gpt_partition(hd, dev, first_sector, minor);
+		} else
+#endif
 		add_partition(hd, minor, first_sector+START_SECT(p), NR_SECTS(p));
 		if (is_extended_partition(p)) {
 			printk(" <");
diff --git a/linux/dev/include/linux/genhd.h b/linux/dev/include/linux/genhd.h
index 20a6c97..f19015d 100644
--- a/linux/dev/include/linux/genhd.h
+++ b/linux/dev/include/linux/genhd.h
@@ -128,6 +128,73 @@ struct bsd_disklabel {
 
 #endif	/* CONFIG_BSD_DISKLABEL */
 
+#ifdef CONFIG_GPT_DISKLABEL
+/*
+ * GPT disklabel support by наб <nabijaczleweli@gmail.com>
+ *
+ * Based on UEFI specification 2.8A (current as of May 2020):
+ * https://uefi.org/specifications
+ * https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf
+ *
+ * CRC32 behaviour (final ^ ~0) courtesy of util-linux documentation:
+ * https://git.kernel.org/pub/scm/utils/util-linux/util-linux.git/tree/libblkid/src/partitions/gpt.c?id=042f62dfc514da177c148c257e4dcb32e5f8379d#n104
+ */
+
+#define GPT_PARTITION		0xee	/* Partition ID in MBR */
+
+#define GPT_GUID_SIZE	16
+struct gpt_guid {
+	__u32	g_time_low;		/* Low field of timestamp */
+	__u16	g_time_mid;		/* Medium field of timestamp */
+	__u16	g_time_high_version;		/* High field of timestamp and version */
+	__u8	g_clock_sec_high;		/* High field of clock sequence and variant */
+	__u8	g_clock_sec_low;		/* Low field of clock sequence */
+	__u8	g_node_id[6];		/* Spatially unique node identifier (MAC address or urandom) */
+} __attribute((packed));
+typedef char __gpt_guid_right_size[(sizeof(struct gpt_guid) == GPT_GUID_SIZE) ? 1 : -1];
+
+static const struct gpt_guid GPT_GUID_TYPE_UNUSED = {0,0,0,0,0,{0,0,0,0,0,0}};
+
+#define GPT_SIGNATURE	"EFI PART"		/* The header signauture */
+#define GPT_REVISION	(0x00010000UL)	/* Little-endian on disk */
+#define GPT_HEADER_SIZE	92
+#define GPT_MAXPARTITIONS	128
+struct gpt_disklabel_header {
+	char	h_signature[8];		/* Must match GPT_SIGNATURE */
+	__u32	h_revision;			/* Disklabel revision, must match GPT_REVISION */
+	__u32	h_header_size;		/* Must match GPT_HEADER_SIZE */
+	__u32	h_header_crc;		/* CRC32 of header, zero for calculation */
+	__u32	h_reserved;		/* Must be zero */
+	__u64	h_lba_current;		/* LBA of this copy of the header */
+	__u64	h_lba_backup;		/* LBA of the second (backup) copy of the header */
+	__u64	h_lba_usable_first;		/* First usable LBA for partitions (last LBA of primary table + 1) */
+	__u64	h_lba_usable_last;		/* Last usable LBA for partitions (first LBA of secondary table - 1) */
+	struct gpt_guid	h_guid;		/* ID of the disk */
+	__u64	h_part_table_lba;		/* First LBA of the partition table (usually 2 in primary header) */
+	__u32	h_part_table_len;		/* Amount of entries in the partition table */
+	__u32	h_part_table_entry_size;		/* Size of each partition entry (usually 128) */
+	__u32	h_part_table_crc;		/* CRC32 of entire partition table, starts at h_part_table_lba, is h_part_table_len*h_part_table_entry_size long */
+						/* Rest of block must be zero */
+} __attribute((packed));
+typedef char __gpt_header_right_size[(sizeof(struct gpt_disklabel_header) == GPT_HEADER_SIZE) ? 1 : -1];
+
+/* 3-47: reserved; 48-63: defined for individual partition types. */
+#define GPT_PARTITION_ATTR_PLATFORM_REQUIRED	(1ULL << 0)		/* Required by the platform to function */
+#define GPT_PARTITION_ATTR_EFI_IGNORE	(1ULL << 1)		/* To be ignored by the EFI firmware */
+#define GPT_PARTITION_ATTR_BIOS_BOOTABLE	(1ULL << 2)		/* Equivalent to MBR active flag */
+
+#define GPT_PARTITION_ENTRY_SIZE	128		/* Minimum size, implementations must respect bigger vendor-specific entries */
+struct gpt_disklabel_part {
+	struct gpt_guid	p_type;		/* Partition type GUID */
+	struct gpt_guid	p_guid;		/* ID of the partition */
+	__u64	p_lba_first;		/* First LBA of the partition */
+	__u64	p_lba_last;		/* Last LBA of the partition */
+	__u64	p_attrs;		/* Partition attribute bitfield, see above */
+	__u16	p_name[36];		/* Display name of partition, UTF-16 */
+} __attribute((packed));
+typedef char __gpt_part_entry_right_size[(sizeof(struct gpt_disklabel_part) == GPT_PARTITION_ENTRY_SIZE) ? 1 : -1];
+#endif	/* CONFIG_GPT_DISKLABEL */
+
 extern struct gendisk *gendisk_head;	/* linked list of disks */
 
 /*
-- 
2.27.0.rc0

Attachment: signature.asc
Description: PGP signature


Reply to: