Module Name:    src
Committed By:   nonaka
Date:           Thu Feb 23 12:17:36 UTC 2017

Modified Files:
        src/sys/arch/x86/include: efi.h
        src/sys/arch/x86/x86: efi.c

Log Message:
Avoid panic when amd64 kernel is booted from 32bit UEFI.


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/x86/include/efi.h
cvs rdiff -u -r1.9 -r1.10 src/sys/arch/x86/x86/efi.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/x86/include/efi.h
diff -u src/sys/arch/x86/include/efi.h:1.5 src/sys/arch/x86/include/efi.h:1.6
--- src/sys/arch/x86/include/efi.h:1.5	Tue Feb 14 13:29:09 2017
+++ src/sys/arch/x86/include/efi.h	Thu Feb 23 12:17:36 2017
@@ -1,4 +1,4 @@
-/*     $NetBSD: efi.h,v 1.5 2017/02/14 13:29:09 nonaka Exp $   */
+/*     $NetBSD: efi.h,v 1.6 2017/02/23 12:17:36 nonaka Exp $   */
 
 /*-
  * Copyright (c) 2004 Marcel Moolenaar
@@ -42,9 +42,6 @@
 extern const struct uuid EFI_UUID_ACPI20;
 extern const struct uuid EFI_UUID_ACPI10;
 
-#define        EFI_TABLE_SAL                           \
-       {0xeb9d2d32,0x2d88,0x11d3,0x9a,0x16,{0x00,0x90,0x27,0x3f,0xc1,0x4d}}
-
 enum efi_reset {
        EFI_RESET_COLD,
        EFI_RESET_WARM
@@ -53,11 +50,11 @@ enum efi_reset {
 typedef uint16_t       efi_char;
 typedef unsigned long efi_status;
 
-
 struct efi_cfgtbl {
        struct uuid     ct_uuid;
        void           *ct_data;
 };
+
 struct efi_md {
        uint32_t        md_type;
 #define        EFI_MD_TYPE_NULL        0
@@ -159,6 +156,53 @@ struct efi_systbl {
        struct efi_cfgtbl *st_cfgtbl;
 };
 
+#if defined(__amd64__)
+struct efi_cfgtbl32 {
+	struct uuid		ct_uuid;
+	uint32_t		ct_data;	/* void * */
+};
+
+struct efi_systbl32 {
+	struct efi_tblhdr	st_hdr;
+
+	uint32_t		st_fwvendor;
+	uint32_t		st_fwrev;
+	uint32_t		st_cin;		/* = 0 */
+	uint32_t		st_cinif;	/* = 0 */
+	uint32_t		st_cout;	/* = 0 */
+	uint32_t		st_coutif;	/* = 0 */
+	uint32_t		st_cerr;	/* = 0 */
+	uint32_t		st_cerrif;	/* = 0 */
+	uint32_t		st_rt;		/* struct efi_rt32 * */
+	uint32_t		st_bs;		/* = 0 */
+	uint32_t		st_entries;
+	uint32_t		st_cfgtbl;	/* struct efi_cfgtbl32 * */
+};
+#elif defined(__i386__)
+struct efi_cfgtbl64 {
+	struct uuid		ct_uuid;
+	uint64_t		ct_data;	/* void * */
+};
+
+struct efi_systbl64 {
+	struct efi_tblhdr	st_hdr;
+
+	uint64_t		st_fwvendor;
+	uint32_t		st_fwrev;
+	uint32_t		__pad;
+	uint64_t		st_cin;		/* = 0 */
+	uint64_t		st_cinif;	/* = 0 */
+	uint64_t		st_cout;	/* = 0 */
+	uint64_t		st_coutif;	/* = 0 */
+	uint64_t		st_cerr;	/* = 0 */
+	uint64_t		st_cerrif;	/* = 0 */
+	uint64_t		st_rt;		/* struct efi_rt64 * */
+	uint64_t		st_bs;		/* = 0 */
+	uint64_t		st_entries;
+	uint64_t		st_cfgtbl;	/* struct efi_cfgtbl64 * */
+};
+#endif
+
 bool               efi_probe(void);
 paddr_t            efi_getsystblpa(void);
 struct efi_systbl *efi_getsystbl(void);

Index: src/sys/arch/x86/x86/efi.c
diff -u src/sys/arch/x86/x86/efi.c:1.9 src/sys/arch/x86/x86/efi.c:1.10
--- src/sys/arch/x86/x86/efi.c:1.9	Thu Feb 16 03:53:20 2017
+++ src/sys/arch/x86/x86/efi.c	Thu Feb 23 12:17:36 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: efi.c,v 1.9 2017/02/16 03:53:20 nonaka Exp $	*/
+/*	$NetBSD: efi.c,v 1.10 2017/02/23 12:17:36 nonaka Exp $	*/
 
 /*-
  * Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.9 2017/02/16 03:53:20 nonaka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.10 2017/02/23 12:17:36 nonaka Exp $");
 
 #include <sys/kmem.h>
 #include <sys/param.h>
@@ -65,7 +65,7 @@ void 		efi_aprintcfgtbl(void);
 void 		efi_aprintuuid(const struct uuid *);
 bool 		efi_uuideq(const struct uuid *, const struct uuid *);
 
-static bool efi_is32bit = false;
+static bool efi_is32x64 = false;
 static struct efi_systbl *efi_systbl_va = NULL;
 static struct efi_cfgtbl *efi_cfgtblhead_va = NULL;
 static struct efi_e820memmap {
@@ -158,7 +158,18 @@ efi_getcfgtblhead(void)
 	if (efi_cfgtblhead_va != NULL)
 		return efi_cfgtblhead_va;
 
-	pa = (paddr_t)(u_long) efi_systbl_va->st_cfgtbl;
+	if (efi_is32x64) {
+#if defined(__amd64__)
+		struct efi_systbl32 *systbl32 = (void *) efi_systbl_va;
+		pa = systbl32->st_cfgtbl;
+#elif defined(__i386__)
+		struct efi_systbl64 *systbl64 = (void *) efi_systbl_va;
+		if (systbl64->st_cfgtbl & 0xffffffff00000000ULL)
+			return NULL;
+		pa = (paddr_t) systbl64->st_cfgtbl;
+#endif
+	} else
+		pa = (paddr_t)(u_long) efi_systbl_va->st_cfgtbl;
 	aprint_debug("efi: cfgtbl at pa %" PRIxPADDR "\n", pa);
 	va = efi_getva(pa);
 	aprint_debug("efi: cfgtbl mapped at va %" PRIxVADDR "\n", va);
@@ -175,7 +186,34 @@ void
 efi_aprintcfgtbl(void)
 {
 	struct efi_cfgtbl *ct;
-	unsigned long 	count;
+	unsigned long count;
+
+	if (efi_is32x64) {
+#if defined(__amd64__)
+		struct efi_systbl32 *systbl32 = (void *) efi_systbl_va;
+		struct efi_cfgtbl32 *ct32 = (void *) efi_cfgtblhead_va;
+
+		count = systbl32->st_entries;
+		aprint_debug("efi: %lu cfgtbl entries:\n", count);
+		for (; count; count--, ct32++) {
+			aprint_debug("efi: %08" PRIx32, ct32->ct_data);
+			efi_aprintuuid(&ct32->ct_uuid);
+			aprint_debug("\n");
+		}
+#elif defined(__i386__)
+		struct efi_systbl64 *systbl64 = (void *) efi_systbl_va;
+		struct efi_cfgtbl64 *ct64 = (void *) efi_cfgtblhead_va;
+		uint64_t count64 = systbl64->st_entries;
+
+		aprint_debug("efi: %" PRIu64 " cfgtbl entries:\n", count64);
+		for (; count64; count64--, ct64++) {
+			aprint_debug("efi: %016" PRIx64, ct64->ct_data);
+			efi_aprintuuid(&ct64->ct_uuid);
+			aprint_debug("\n");
+		}
+#endif
+		return;
+	}
 
 	ct = efi_cfgtblhead_va;
 	count = efi_systbl_va->st_entries;
@@ -195,7 +233,7 @@ void *
 efi_getcfgtbl(const struct uuid * uuid)
 {
 	paddr_t pa;
-	vaddr_t  va;
+	vaddr_t va;
 
 	pa = efi_getcfgtblpa(uuid);
 	if (pa == 0)
@@ -213,6 +251,28 @@ efi_getcfgtblpa(const struct uuid * uuid
 	struct efi_cfgtbl *ct;
 	unsigned long count;
 
+	if (efi_is32x64) {
+#if defined(__amd64__)
+		struct efi_systbl32 *systbl32 = (void *) efi_systbl_va;
+		struct efi_cfgtbl32 *ct32 = (void *) efi_cfgtblhead_va;
+
+		count = systbl32->st_entries;
+		for (; count; count--, ct32++)
+			if (efi_uuideq(&ct32->ct_uuid, uuid))
+				return ct32->ct_data;
+#elif defined(__i386__)
+		struct efi_systbl64 *systbl64 = (void *) efi_systbl_va;
+		struct efi_cfgtbl64 *ct64 = (void *) efi_cfgtblhead_va;
+		uint64_t count64 = systbl64->st_entries;
+
+		for (; count64; count64--, ct64++)
+			if (efi_uuideq(&ct64->ct_uuid, uuid))
+				if (!(ct64->ct_data & 0xffffffff00000000ULL))
+					return ct64->ct_data;
+#endif
+		return 0;	/* Not found. */
+	}
+
 	ct = efi_cfgtblhead_va;
 	count = efi_systbl_va->st_entries;
 	for (; count; count--, ct++)
@@ -234,15 +294,23 @@ efi_getsystblpa(void)
 		/* Unable to locate the EFI System Table. */
 		return 0;
 	}
+	if (sizeof(paddr_t) == 4 &&	/* XXX i386 with PAE */
+	    (bi->systblpa & 0xffffffff00000000ULL)) {
+		/* Unable to access EFI System Table. */
+		return 0;
+	}
 	if (bi->common.len > 16 && (bi->flags & BI_EFI_32BIT)) {
-		/* 32Bit UEFI */
-		if (sizeof(paddr_t) == 4 &&
-		    (bi->systblpa & 0xffffffff00000000ULL))
-			/* Unable to access EFI System Table. */
-			return 0;
-		efi_is32bit = true;
+		/* boot from 32bit UEFI */
+#if defined(__amd64__)
+		efi_is32x64 = true;
+#endif
+	} else {
+		/* boot from 64bit UEFI */
+#if defined(__i386__)
+		efi_is32x64 = true;
+#endif
 	}
-	pa = (paddr_t)bi->systblpa;
+	pa = (paddr_t) bi->systblpa;
 	return pa;
 }
 
@@ -253,34 +321,78 @@ efi_getsystblpa(void)
 struct efi_systbl *
 efi_getsystbl(void)
 {
-	if (!efi_systbl_va) {
-		paddr_t 	pa;
-		vaddr_t 	va;
-		struct efi_systbl *systbl;
+	paddr_t pa;
+	vaddr_t va;
+	struct efi_systbl *systbl;
+
+	if (efi_systbl_va)
+		return efi_systbl_va;
+
+	pa = efi_getsystblpa();
+	if (pa == 0)
+		return NULL;
+
+	aprint_normal("efi: systbl at pa %" PRIxPADDR "\n", pa);
+	va = efi_getva(pa);
+	aprint_debug("efi: systbl mapped at va %" PRIxVADDR "\n", va);
+
+	if (efi_is32x64) {
+#if defined(__amd64__)
+		struct efi_systbl32 *systbl32 = (struct efi_systbl32 *) va;
 
-		pa = efi_getsystblpa();
-		if (pa == 0)
-			return NULL;
-		aprint_normal("efi: systbl at pa %" PRIxPADDR "\n", pa);
-		va = efi_getva(pa);
-		aprint_debug("efi: systbl mapped at va %" PRIxVADDR "\n", va);
-		systbl = (struct efi_systbl *) va;
 		/* XXX Check the signature and the CRC32 */
 		aprint_debug("efi: signature %" PRIx64 " revision %" PRIx32
-		    " crc32 %" PRIx32 "\n", systbl->st_hdr.th_sig,
-		    systbl->st_hdr.th_rev, systbl->st_hdr.th_crc32);
+		    " crc32 %" PRIx32 "\n", systbl32->st_hdr.th_sig,
+		    systbl32->st_hdr.th_rev, systbl32->st_hdr.th_crc32);
 		aprint_debug("efi: firmware revision %" PRIx32 "\n",
-		    systbl->st_fwrev);
+		    systbl32->st_fwrev);
 		/*
 		 * XXX Also print fwvendor, which is an UCS-2 string (use
 		 * some UTF-16 routine?)
 		 */
-		aprint_debug("efi: runtime services at pa %p\n",
-		    systbl->st_rt);
-		aprint_debug("efi: boot services at pa %p\n",
-		    systbl->st_bs);
-		efi_systbl_va = systbl;
+		aprint_debug("efi: runtime services at pa 0x%08" PRIx32 "\n",
+		    systbl32->st_rt);
+		aprint_debug("efi: boot services at pa 0x%08" PRIx32 "\n",
+		    systbl32->st_bs);
+
+		efi_systbl_va = (struct efi_systbl *) systbl32;
+#elif defined(__i386__)
+		struct efi_systbl64 *systbl64 = (struct efi_systbl64 *) va;
+
+		/* XXX Check the signature and the CRC32 */
+		aprint_debug("efi: signature %" PRIx64 " revision %" PRIx32
+		    " crc32 %" PRIx32 "\n", systbl64->st_hdr.th_sig,
+		    systbl64->st_hdr.th_rev, systbl64->st_hdr.th_crc32);
+		aprint_debug("efi: firmware revision %" PRIx32 "\n",
+		    systbl64->st_fwrev);
+		/*
+		 * XXX Also print fwvendor, which is an UCS-2 string (use
+		 * some UTF-16 routine?)
+		 */
+		aprint_debug("efi: runtime services at pa 0x%016" PRIx64 "\n",
+		    systbl64->st_rt);
+		aprint_debug("efi: boot services at pa 0x%016" PRIx64 "\n",
+		    systbl64->st_bs);
+
+		efi_systbl_va = (struct efi_systbl *) systbl64;
+#endif
+		return efi_systbl_va;
 	}
+
+	systbl = (struct efi_systbl *) va;
+	/* XXX Check the signature and the CRC32 */
+	aprint_debug("efi: signature %" PRIx64 " revision %" PRIx32
+	    " crc32 %" PRIx32 "\n", systbl->st_hdr.th_sig,
+	    systbl->st_hdr.th_rev, systbl->st_hdr.th_crc32);
+	aprint_debug("efi: firmware revision %" PRIx32 "\n", systbl->st_fwrev);
+	/*
+	 * XXX Also print fwvendor, which is an UCS-2 string (use
+	 * some UTF-16 routine?)
+	 */
+	aprint_debug("efi: runtime services at pa %p\n", systbl->st_rt);
+	aprint_debug("efi: boot services at pa %p\n", systbl->st_bs);
+
+	efi_systbl_va = systbl;
 	return efi_systbl_va;
 }
 
@@ -290,11 +402,11 @@ efi_getsystbl(void)
 bool
 efi_probe(void)
 {
-	if (efi_getsystbl() == 0) {
+	if (efi_getsystbl() == NULL) {
 		aprint_debug("efi: missing or invalid systbl\n");
 		return false;
 	}
-	if (efi_getcfgtblhead() == 0) {
+	if (efi_getcfgtblhead() == NULL) {
 		aprint_debug("efi: missing or invalid cfgtbl\n");
 		efi_relva((vaddr_t) efi_systbl_va);
 		return false;

Reply via email to