Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 10 Jun 2012 22:48:13 +0900 (JST)
From:      Hiroki Sato <hrs@FreeBSD.org>
To:        current@FreeBSD.org
Cc:        wblock@wonkity.com, ae@FreeBSD.org, jhb@FreeBSD.org
Subject:   CFR: backup GPT header support in pmbr and loader(8) (Re: Handbook mirroring section)
Message-ID:  <20120610.224813.710171778841273502.hrs@allbsd.org>
In-Reply-To: <4FD05573.70801@FreeBSD.org>
References:  <4FCF3021.5070802@FreeBSD.org> <20120606.200735.1551208261335301113.hrs@allbsd.org> <4FD05573.70801@FreeBSD.org>

next in thread | previous in thread | raw e-mail | index | archive | help
----Security_Multipart0(Sun_Jun_10_22_48_13_2012_513)--
Content-Type: Multipart/Mixed;
	boundary="--Next_Part(Sun_Jun_10_22_48_13_2012_239)--"
Content-Transfer-Encoding: 7bit

----Next_Part(Sun_Jun_10_22_48_13_2012_239)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

[move from -doc@ to -current@]

"Andrey V. Elsukov" <ae@FreeBSD.org> wrote
  in <4FD05573.70801@FreeBSD.org>:

ae> On 06.06.2012 15:07, Hiroki Sato wrote:
ae> > ae> 1. When geom_mirror module is not loaded GEOM_PART will complain that the
ae> > ae> backup GPT header is not in the last LBA and partition table will be marked
ae> > ae> as CORRUPT. The recover operation will destroy the GEOM_MIRROR's metadata.
ae> > ae>
ae> > ae> 2. If primary GPT header or table become damaged, then gptboot will not
ae> > ae> detect GPT because the backup GPT header is not in the last LBA. So, the
ae> > ae> system will not boot.
ae> >
ae> >  Thanks, I see.  Do you think the attached patch is too aggressive for
ae> >  the problem 2?  The value of altlba should be matched with one in the
ae> >  original secondary header when the primary header is corrupted and
ae> >  the secondary header is looked up in this way.
ae>
ae> Yes, i also have thought about this and this should work for most GEOM classes,
ae> but i revised again PMBR code and it seems that it will not work anyway :)
ae> Our PMBR doesn't use backup GPT header and table, and it doesn't verify
ae> correctness of primary GPT.
ae>
ae> From the other side, there are three situations when we use GPT:
ae> 1. FreeBSD is only one system on the disk and we use PMBR and gptboot to boot it.
ae>
ae> In case if we will fix PMBR your patch will help.

 I created the attached patchset for the loader and pmbr to support
 backup GPT header when the primary one is corrupted.  Can anyone test
 and/or review it?

 The pmbr program now checks the GPT signature, and if failed it tries
 to search the backup header from the last LBA.  When GEOM metadata is
 found at the last LBA, the second last will be checked.  The
 loader(8) program also supports the same algorithm to search the
 backup header.

ae> 2. FreeBSD is no one on the disk, but we still use legacy boot method (PMBR+gptboot).
ae>
ae> I don't know what behavior have other systems when they detect invalid GPT (i.e.
ae> when backup header is not in the last LBA).
ae>
ae> 3. We use UEFI (it is not work yet).
ae>
ae> Also i don't know what UEFI firmware will do with invalid GPT.

 I am investigating how Windows and other UEFI-based software
 recognize the backup header now.  Hopefully they use the alternative
 LBA field and do not unconditionally overwrite the last LBA for
 recovering.  Probably it is difficult to make UEFI recognize the
 backup header that is not located at the last LBA when the primary
 header is corrupted.

-- Hiroki

----Next_Part(Sun_Jun_10_22_48_13_2012_239)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="boot-gpt.20120610-1.diff"

Index: common/gpt.c
===================================================================
--- common/gpt.c	(revision 236848)
+++ common/gpt.c	(working copy)
@@ -40,6 +40,7 @@
 #include "gpt.h"

 #define	MAXTBLENTS	128
+#define	GEOM_MAGIC	"GEOM::"

 static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr;
 static uint64_t hdr_primary_lba, hdr_backup_lba;
@@ -345,8 +346,18 @@
 		altlba = hdr_primary.hdr_lba_alt;
 	} else {
 		altlba = drvsize(dskp);
-		if (altlba > 0)
-			altlba--;
+		if (altlba > 0) {
+			do {
+				altlba--;
+				/*
+				 * Check GEOM metadata and decrement
+				 * the altlba if found.
+				 */
+				if (drvread(dskp, secbuf, altlba, 1) != 0)
+					break;
+			} while (memcmp(secbuf, GEOM_MAGIC,
+				    sizeof(GEOM_MAGIC) - 1) == 0);
+		}
 	}
 	if (altlba == 0)
 		printf("%s: unable to locate backup GPT header\n", BOOTPROG);
Index: i386/pmbr/pmbr.s
===================================================================
--- i386/pmbr/pmbr.s	(revision 236848)
+++ i386/pmbr/pmbr.s	(working copy)
@@ -42,8 +42,9 @@
 		.set STACK,EXEC+SECSIZE*4	# Stack address
 		.set GPT_ADDR,STACK		# GPT header address
 		.set GPT_SIG,0
-		.set GPT_SIG_0,0x20494645
-		.set GPT_SIG_1,0x54524150
+		.set GPT_SIG_0,0x20494645	# "EFI "
+		.set GPT_SIG_1,0x54524150	# "PART"
+		.set GEOM_MAGIC,0x4d4f4547	# "GEOM"
 		.set GPT_MYLBA,24
 		.set GPT_PART_LBA,72
 		.set GPT_NPART,80
@@ -52,6 +53,8 @@
 		.set PART_TYPE,0
 		.set PART_START_LBA,32
 		.set PART_END_LBA,40
+		.set DPBUF,PART_ADDR+SECSIZE
+		.set DPBUF_SEC,0x10		# Number of sectors

 		.set NHRDRV,0x475		# Number of hard drives

@@ -91,16 +94,40 @@
 		jb main.2			# Yes
 main.1: 	movb $0x80,%dl			# Assume drive 0x80
 #
-# Load the primary GPT header from LBA 1 and verify signature.
+# Load the GPT header and verify signature.  Try LBA 1 for the primary one and
+# the last LBA for the backup if it is broken.  Skip the LBAs if GEOM
+# metadata found at the backup location.
 #
-main.2:		movw $GPT_ADDR,%bx
+main.2:		call getdrvparams		# Read drive parameters
+		movb $1,%dh			# %dh := 1 (reading primary)
+main.2a:	movw $GPT_ADDR,%bx
 		movw $lba,%si
-		call read
+		call read			# Read header and check GPT sig
 		cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG
-		jnz err_pt
+		jnz main.2b
 		cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4
-		jnz err_pt
+		jnz main.2b
+		jmp load_part
+main.2b:	cmpb $1,%dh			# Reading primary?
+		je main.3			# Try backup if yes
+		cmpl $GEOM_MAGIC,GPT_ADDR	# GEOM sig at backup location?
+		jz main.3			# Skip GEOM metadata
+		jmp err_pt			# Invalid table found
 #
+# Try alternative LBAs from the last sector for the GPT header.
+#
+main.3:		movb $0,%dh			# %dh := 0 (reading backup)
+		movw $DPBUF+DPBUF_SEC,%si	# %si = last sector + 1
+		movw $lba,%di			# %di = $lba
+		cmpl $0,(%si)			#
+		jnz main.3a			#
+		decl 0x4(%si)			# 0x4(%si) = last sec (32-64)
+main.3a:	decl (%si)			# 0x0(%si) = last sec (0-31)
+		movw $2,%cx
+		rep
+		movsw				# $lastsec--, copy it to $lba
+		jmp main.2a			# Read the next sector
+#
 # Load a partition table sector from disk and look for a FreeBSD boot
 # partition.
 #
@@ -172,6 +199,16 @@
 		jc err_rd			# If error
 		ret
 #
+# Check the number of LBAs on the drive index %dx.  Trashes %ax and %si.
+#
+getdrvparams:
+		movw $DPBUF,%si			# Set the address of result buf
+		movw $0x001e,(%si)		# len
+		movw $0x4800,%ax		# BIOS: Read Drive Parameters
+		int $0x13			# Call the BIOS
+		jc err_rd			# "I/O error" if error
+		ret
+#
 # Various error message entry points.
 #
 err_big: 	movw $msg_big,%si		# "Boot loader too

----Next_Part(Sun_Jun_10_22_48_13_2012_239)----

----Security_Multipart0(Sun_Jun_10_22_48_13_2012_513)--
Content-Type: application/pgp-signature
Content-Transfer-Encoding: 7bit

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (FreeBSD)

iEUEABECAAYFAk/UpZ0ACgkQTyzT2CeTzy2EuQCYvKwHIbw1KxbtwJysEYyC9uyL
4ACdHIhJkm8atN0qobTepmZNI1GiNpM=
=bJDW
-----END PGP SIGNATURE-----

----Security_Multipart0(Sun_Jun_10_22_48_13_2012_513)----



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120610.224813.710171778841273502.hrs>