tech-kern archive

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

Boot on GPT in RAID [PATCH]



Hello 

Some time ago, I added code in x86 secondary bootstrap to allow
booting on GPT partitions inside a RAID 1. It seems I forgot to
commit the support in primary bootstrap, and in the meantime I
deleted the tree with it. I just rewrote the thing, the patch
is below for comment.

The boot priority is as described in x86/boot(8): 
1) the first parition with bootme attribute set
2) the partition we booted from, irrelevant here
3) the first bootable partition (FFS, LFS, CCD, CGD) 
4) the first partititon

I handled the case where GPT entries are not 128 bytes long,
as UEFI specification allows that. That makes the code larger
and painful to read. Is it worth it?

Index: sys/arch/i386/stand/bootxx/boot1.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/stand/bootxx/boot1.c,v
retrieving revision 1.21
diff -U4 -r1.21 boot1.c
--- sys/arch/i386/stand/bootxx/boot1.c	24 Jun 2021 01:23:16 -0000	1.21
+++ sys/arch/i386/stand/bootxx/boot1.c	26 Jun 2023 14:31:22 -0000
@@ -36,10 +36,12 @@
 #include <lib/libkern/libkern.h>
 #include <biosdisk_ll.h>
 
 #include <sys/param.h>
+#include <sys/uuid.h>
 #include <sys/bootblock.h>
 #include <sys/disklabel.h>
+#include <sys/disklabel_gpt.h>
 #include <dev/raidframe/raidframevar.h>	/* For RF_PROTECTED_SECTORS */
 
 #define XSTR(x) #x
 #define STR(x) XSTR(x)
@@ -48,8 +50,9 @@
 
 static struct biosdisk_ll d;
 
 const char *boot1(uint32_t, uint64_t *);
+static daddr_t gpt_lookup(daddr_t);
 extern void putstr(const char *);
 
 extern struct disklabel ptn_disklabel;
 
@@ -89,8 +92,17 @@
 	bios_sector += RF_PROTECTED_SECTORS;
 	fd = ob();
 	if (fd != -1)
 		goto done;
+
+	/*
+	 * Test for a GPT inside the RAID
+	 */
+	bios_sector += gpt_lookup(bios_sector);
+	fd = ob();
+	if (fd != -1)
+		goto done;
+
 	/*
 	 * Nothing at the start of the MBR partition, fallback on
 	 * partition 'a' from the disklabel in this MBR partition.
 	 */
@@ -143,4 +155,145 @@
 		return EIO;
 
 	return 0;
 }
+
+static int
+is_unused(struct gpt_ent *ent)
+{
+	const struct uuid unused = GPT_ENT_TYPE_UNUSED;
+
+	return (memcmp(ent->ent_type, &unused, sizeof(unused)) == 0);
+}
+
+static int
+is_bootable(struct gpt_ent *ent)
+{
+	/* GPT_ENT_TYPE_NETBSD_RAID omitted as we are already in a RAID */
+	const struct uuid bootable[] = {
+		GPT_ENT_TYPE_NETBSD_FFS,
+		GPT_ENT_TYPE_NETBSD_LFS,
+		GPT_ENT_TYPE_NETBSD_CCD,
+		GPT_ENT_TYPE_NETBSD_CGD,
+	};
+	int i;
+
+	for (i = 0; i < sizeof(bootable) / sizeof(*bootable); i++) {
+		if (memcmp(ent->ent_type, &bootable[i],
+		    sizeof(struct uuid)) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static daddr_t
+gpt_lookup(daddr_t sector)
+{
+	char buf[DEV_BSIZE];
+	struct mbr_sector *pmbr;	
+	const char gpt_hdr_sig[] = GPT_HDR_SIG;
+	struct gpt_hdr *hdr;
+	struct gpt_ent *ent;
+	uint32_t nents;
+	uint32_t entsz;
+	uint32_t entries_per_sector;
+	uint32_t sectors_per_entry;
+	uint64_t firstpart_lba = 0;
+	uint64_t bootable_lba = 0;
+	uint64_t bootme_lba = 0;
+	int i, j;
+
+	/*
+	 * Look for a PMBR
+	 */
+	if (readsects(&d, sector, 1, buf, 1) != 0)
+		return 0;
+
+	pmbr = (struct mbr_sector *)buf;
+
+	if (pmbr->mbr_magic != htole16(MBR_MAGIC))
+		return 0;
+
+	if (pmbr->mbr_parts[0].mbrp_type != MBR_PTYPE_PMBR)
+		return 0;
+
+	sector++; /* skip PMBR */
+
+	/*
+	 * Look for a GPT header
+	 * Space is scarce, we do not check CRC.
+	 */
+	if (readsects(&d, sector, 1, buf, 1) != 0)
+		return 0;
+
+	hdr = (struct gpt_hdr *)buf;
+
+	if (memcmp(gpt_hdr_sig, hdr->hdr_sig, sizeof(hdr->hdr_sig)) != 0)
+		return 0;
+
+	if (hdr->hdr_revision != htole32(GPT_HDR_REVISION))
+		return 0;
+
+	if (le32toh(hdr->hdr_size) > DEV_BSIZE)
+		return 0;
+
+	nents = le32toh(hdr->hdr_entries);
+	entsz = le32toh(hdr->hdr_entsz);
+
+	sector++; /* skip GPT header */
+
+	/*
+	 * Read partition table
+	 *
+	 * According to UEFI specification, entries are 128 * (2^n)
+	 * bytes long. The most common scenario is 128 bytes (n = 0)
+	 * where there are 4 entries per sector. If n > 2, then entries
+	 * spans multiple sectors, but they remain sector-aligned.
+	 */
+	entries_per_sector = DEV_BSIZE / entsz;
+	if (entries_per_sector == 0)
+		entries_per_sector = 1;
+
+	sectors_per_entry = entsz / DEV_BSIZE;
+	if (sectors_per_entry == 0)
+		sectors_per_entry = 1;
+
+	for (i = 0; i < nents; i += entries_per_sector) {
+		if (readsects(&d, sector, 1, buf, 1) != 0)
+			return 0;
+
+		sector += sectors_per_entry;
+
+		for (j = 0; j < entries_per_sector; j++) {
+			ent = (struct gpt_ent *)&buf[j * entsz];
+
+			if (is_unused(ent))
+				continue;
+
+			/* First bootme wins, we can stop there */
+			if (ent->ent_attr & GPT_ENT_ATTR_BOOTME) {
+				bootme_lba = le64toh(ent->ent_lba_start);
+				goto out;
+			}
+
+			if (firstpart_lba == 0)
+				firstpart_lba = le64toh(ent->ent_lba_start);
+
+			if (is_bootable(ent) && bootable_lba == 0)
+				bootable_lba = le64toh(ent->ent_lba_start);
+		}
+	}
+
+out:
+	if (bootme_lba)
+		return bootme_lba;
+
+	if (bootable_lba)
+		return bootable_lba;
+
+	if (firstpart_lba)
+		return firstpart_lba;
+
+	return 0;
+}

-- 
Emmanuel Dreyfus
manu%netbsd.org@localhost


Home | Main Index | Thread Index | Old Index