Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 20 Sep 2024 14:59:21 GMT
From:      Warner Losh <imp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: f8ca5d45c3c1 - main - stand: Add support for 64-bit machines with 32-bit UEFI implementations
Message-ID:  <202409201459.48KExLwl046755@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=f8ca5d45c3c1829759ecd87cb95d53e5ab7d0811

commit f8ca5d45c3c1829759ecd87cb95d53e5ab7d0811
Author:     Ahmad Khalifa <ahmadkhalifa570@gmail.com>
AuthorDate: 2024-05-14 19:40:06 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2024-09-20 14:45:08 +0000

    stand: Add support for 64-bit machines with 32-bit UEFI implementations
    
    Some machines have 64-bit capable cpus but are stuck on 32-bit uefi
    firmware.
    
    Add support for them by building a new "loader_ia32" with
    LOADER_DEFAULT_INTERP along with the 64-bit one. The loader
    can be disabled using MK_LOADER_IA32.
    
    Reviewed by: imp
    Pull Request: https://github.com/freebsd/freebsd-src/pull/1098
---
 stand/common/bootstrap.h                   |   2 +-
 stand/common/load_elf.c                    |   4 +-
 stand/efi/Makefile                         |   1 +
 stand/efi/Makefile.inc                     |   4 +
 stand/efi/loader/Makefile                  |  24 ++-
 stand/efi/loader/arch/i386/Makefile.inc    |   9 +
 stand/efi/loader/arch/i386/amd64_tramp.S   | 100 +++++++++++
 stand/efi/loader/arch/i386/elf64_freebsd.c | 274 +++++++++++++++++++++++++++++
 stand/efi/loader/arch/i386/i386.ldscript   |  55 ++++++
 stand/efi/loader/arch/i386/setup.c         |  51 ++++++
 stand/efi/loader/arch/i386/start.S         |  75 ++++++++
 stand/efi/loader/bootinfo.c                |  13 +-
 stand/efi/loader/conf.c                    |   4 +-
 stand/efi/loader/copy.c                    |  18 +-
 stand/efi/loader/loader_efi.h              |   2 +-
 stand/efi/loader/main.c                    |   2 +-
 stand/efi/loader_ia32/Makefile             |   6 +
 17 files changed, 621 insertions(+), 23 deletions(-)

diff --git a/stand/common/bootstrap.h b/stand/common/bootstrap.h
index 09c4832f5855..ab1e5249fc2a 100644
--- a/stand/common/bootstrap.h
+++ b/stand/common/bootstrap.h
@@ -239,7 +239,7 @@ struct preloaded_file
 	size_t f_size;		/* file size */
 	struct kernel_module	*f_modules;	/* list of modules if any */
 	struct preloaded_file	*f_next;	/* next file */
-#ifdef __amd64__
+#if defined(__amd64__) || defined(__i386__)
 	bool			f_kernphys_relocatable;
 #endif
 #if defined(__i386__)
diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c
index 8877e5f8b7e0..eaa6bef6ee86 100644
--- a/stand/common/load_elf.c
+++ b/stand/common/load_elf.c
@@ -217,7 +217,7 @@ static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr)
 }
 #endif
 
-#ifdef __amd64__
+#if defined(__amd64__) || defined(__i386__)
 static bool
 is_kernphys_relocatable(elf_file_t ef)
 {
@@ -491,7 +491,7 @@ __elfN(loadfile_raw)(char *filename, uint64_t dest,
 	/* Load OK, return module pointer */
 	*result = (struct preloaded_file *)fp;
 	err = 0;
-#ifdef __amd64__
+#if defined(__amd64__) || defined(__i386__)
 	fp->f_kernphys_relocatable = multiboot || is_kernphys_relocatable(&ef);
 #endif
 #ifdef __i386__
diff --git a/stand/efi/Makefile b/stand/efi/Makefile
index 1a9cf68749a1..1887b9536a5b 100644
--- a/stand/efi/Makefile
+++ b/stand/efi/Makefile
@@ -11,6 +11,7 @@ SUBDIR.yes+=	boot1 gptboot
 
 SUBDIR.${MK_FORTH}+= loader_4th
 SUBDIR.${MK_LOADER_LUA}+= loader_lua
+SUBDIR.${MK_LOADER_IA32}+= loader_ia32
 SUBDIR.yes+=	loader_simp
 
 .include <bsd.subdir.mk>
diff --git a/stand/efi/Makefile.inc b/stand/efi/Makefile.inc
index 18751f0e4ecc..b5bd341d1b78 100644
--- a/stand/efi/Makefile.inc
+++ b/stand/efi/Makefile.inc
@@ -18,7 +18,11 @@ CFLAGS+=	-fPIC
 .endif
 
 .if ${MACHINE_CPUARCH} == "amd64"
+.if ${DO32:U0} == 1
+EFI_TARGET=	efi-app-ia32
+.else
 EFI_TARGET=	efi-app-x86_64
+.endif
 .else
 EFI_TARGET=	binary
 .endif
diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
index 8f6802831824..ae25910da2c5 100644
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -6,7 +6,13 @@ LOADER_EXT2FS_SUPPORT?=	no
 
 .include <bsd.init.mk>
 
+.if ${MACHINE} == "amd64" && ${DO32:U0} == 1
+__arch=	i386
+LOADER?=	loader_ia32
+.else
+__arch=	${MACHINE}
 LOADER?=	loader_${LOADER_INTERP}
+.endif
 PROG=		${LOADER}.sym
 INTERNALPROG=
 WARNS?=		3
@@ -52,13 +58,13 @@ CFLAGS.gfx_fb.c += -DHAVE_MEMCPY -I${SRCTOP}/sys/contrib/zlib
 CWARNFLAGS.main.c+=	-Wno-format
 
 .PATH: ${.CURDIR}/../loader
-.PATH: ${.CURDIR}/../loader/arch/${MACHINE}
-.include "${.CURDIR}/../loader/arch/${MACHINE}/Makefile.inc"
+.PATH: ${.CURDIR}/../loader/arch/${__arch}
+.include "${.CURDIR}/../loader/arch/${__arch}/Makefile.inc"
 
 CFLAGS+=	-I${.CURDIR}
-CFLAGS+=	-I${.CURDIR}/arch/${MACHINE}
+CFLAGS+=	-I${.CURDIR}/arch/${__arch}
 CFLAGS+=	-I${EFISRC}/include
-CFLAGS+=	-I${EFISRC}/include/${MACHINE}
+CFLAGS+=	-I${EFISRC}/include/${__arch}
 CFLAGS+=	-I${SYSDIR}/contrib/dev/acpica/include
 CFLAGS+=	-I${BOOTSRC}/i386/libi386
 CFLAGS+=	-DEFI
@@ -95,11 +101,11 @@ CLEANFILES+=	8x16.c
 FILES+=	${LOADER}.efi
 FILESMODE_${LOADER}.efi=	${BINMODE}
 
-.if ${LOADER_INTERP} == ${LOADER_DEFAULT_INTERP}
+.if ${LOADER_INTERP} == ${LOADER_DEFAULT_INTERP} && ${__arch} != "i386"
 LINKS+=		${BINDIR}/${LOADER}.efi ${BINDIR}/loader.efi
 .endif
 
-LDSCRIPT=	${.CURDIR}/../loader/arch/${MACHINE}/${MACHINE}.ldscript
+LDSCRIPT=	${.CURDIR}/../loader/arch/${__arch}/${__arch}.ldscript
 LDFLAGS+=	-Wl,-T${LDSCRIPT},-Bsymbolic,-znotext -pie
 .if ${LINKER_TYPE} == "bfd" && ${LINKER_VERSION} >= 23400
 LDFLAGS+=	-Wl,--no-dynamic-linker
@@ -125,8 +131,14 @@ ${LOADER}.efi: ${PROG}
 		--output-target=${EFI_TARGET} ${.ALLSRC} ${.TARGET}
 
 LIBEFI=		${BOOTOBJ}/efi/libefi/libefi.a
+LIBEFI32=	${BOOTOBJ}/efi/libefi32/libefi.a
 
+.if ${__arch} == "i386"
+DPADD=		${LDR_INTERP32} ${LIBEFI32} ${LIBSA32} ${LDSCRIPT}
+LDADD=		${LDR_INTERP32} ${LIBEFI32} ${LIBSA32}
+.else
 DPADD=		${LDR_INTERP} ${LIBEFI} ${LIBSAFDT} ${LIBEFI_FDT} ${LIBSA} ${LDSCRIPT}
 LDADD=		${LDR_INTERP} ${LIBEFI} ${LIBSAFDT} ${LIBEFI_FDT} ${LIBSA}
+.endif
 
 .include <bsd.prog.mk>
diff --git a/stand/efi/loader/arch/i386/Makefile.inc b/stand/efi/loader/arch/i386/Makefile.inc
new file mode 100644
index 000000000000..e27b553c6bab
--- /dev/null
+++ b/stand/efi/loader/arch/i386/Makefile.inc
@@ -0,0 +1,9 @@
+SRCS+=	amd64_tramp.S \
+	start.S \
+	setup.c \
+	elf64_freebsd.c
+
+.PATH: ${BOOTSRC}/i386/libi386
+SRCS+=	nullconsole.c \
+	comconsole.c \
+	spinconsole.c
diff --git a/stand/efi/loader/arch/i386/amd64_tramp.S b/stand/efi/loader/arch/i386/amd64_tramp.S
new file mode 100644
index 000000000000..06f9262014b3
--- /dev/null
+++ b/stand/efi/loader/arch/i386/amd64_tramp.S
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * Copyright (c) 2023 Ahmad Khalifa <ahmadkhalifa570@gmail.com>
+ *
+ * This software was developed by Benno Rice under sponsorship from
+ * the FreeBSD Foundation.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asmacros.h>
+
+	.text
+	.globl	amd64_tramp
+
+/*
+ * void amd64_tramp(uint32_t stack, void *copy_finish, uint32_t kernend,
+ * 		    uint32_t modulep, uint32_t pagetable, uint32_t gdtr, uint64_t entry)
+ */
+amd64_tramp:
+	cli	/* Make sure we don't get interrupted. */
+
+	calll *8(%esp)	/* Call copy_finish so we're all ready to go. */
+
+	movl %cr0, %eax	/* Paging may be enabled, disable it. */
+	andl $0x7FFFFFFF, %eax
+	movl %eax, %cr0
+
+	movl %cr4, %eax	/* PAE may be disabled, enable it. */
+	orl $0x20, %eax
+	movl %eax, %cr4
+
+	movl 20(%esp), %eax	/* Swap page tables. */
+	movl %eax, %cr3
+
+	movl $0xC0000080, %ecx	/* Enable long mode. */
+	rdmsr
+	orl $0x100, %eax
+	wrmsr
+
+	movl 12(%esp), %edi	/* Stash the kernel and GDT values for later. */
+	movl 16(%esp), %esi
+	movl 24(%esp), %ebx
+	movl 28(%esp), %edx
+	movl 32(%esp), %ebp
+
+	movl 4(%esp), %esp	/* Switch to our temporary stack. */
+
+	movl %cr0, %eax	/* Enable paging and enter compatibility mode. */
+	orl $0x80000000, %eax
+	movl %eax, %cr0
+
+	lgdtl (%ebx)	/* Load GDT. */
+
+	pushl %edi	/* Push kernend. */
+	pushl %esi	/* Push modulep. */
+	pushl $0x0
+	pushl %ebp	/* Push 64-bit entry address. */
+	pushl %edx
+
+	calll 0f	/* Find the address of ".longmode". */
+0:	popl %eax
+	addl $(.longmode-0b), %eax
+
+	pushl $0x8	/* Push CS. */
+	pushl %eax	/* Push the address. */
+	lretl	/* "Return" to 64-bit code. */
+
+	.code64
+
+.longmode:
+	retq	/* "Return" to kernel entry. */
+
+	.code32
+
+	ALIGN_TEXT
+amd64_tramp_end:
+
+	.data
+	.globl	amd64_tramp_size
+amd64_tramp_size:
+	.long	amd64_tramp_end-amd64_tramp
diff --git a/stand/efi/loader/arch/i386/elf64_freebsd.c b/stand/efi/loader/arch/i386/elf64_freebsd.c
new file mode 100644
index 000000000000..ba5117b37016
--- /dev/null
+++ b/stand/efi/loader/arch/i386/elf64_freebsd.c
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define __ELF_WORD_SIZE 64
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <machine/elf.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+
+#include "loader_efi.h"
+
+extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp,
+    bool exit_bs);
+
+static int	elf64_exec(struct preloaded_file *amp);
+static int	elf64_obj_exec(struct preloaded_file *amp);
+
+static struct file_format amd64_elf = {
+	.l_load = elf64_loadfile,
+	.l_exec = elf64_exec
+};
+
+static struct file_format amd64_elf_obj = {
+	.l_load = elf64_obj_loadfile,
+	.l_exec = elf64_obj_exec
+};
+
+struct file_format *file_formats[] = {
+	&amd64_elf,
+	&amd64_elf_obj,
+	NULL
+};
+
+struct gdtr {
+	uint16_t size;
+	uint64_t ptr;
+} __packed;
+
+#define PG_V	0x001
+#define PG_RW	0x002
+#define PG_PS	0x080
+
+#define GDT_P	0x00800000000000
+#define GDT_E	0x00080000000000
+#define GDT_S	0x00100000000000
+#define GDT_RW	0x00020000000000
+#define GDT_L	0x20000000000000
+
+#define M(x)	((x) * 1024 * 1024)
+#define G(x)	(1ULL * (x) * 1024 * 1024 * 1024)
+
+typedef uint64_t p4_entry_t;
+typedef uint64_t p3_entry_t;
+typedef uint64_t p2_entry_t;
+typedef uint64_t gdt_t;
+
+static p4_entry_t *PT4;
+static p3_entry_t *PT3;
+static p3_entry_t *PT3_l, *PT3_u;
+static p2_entry_t *PT2;
+static p2_entry_t *PT2_l0, *PT2_l1, *PT2_l2, *PT2_l3, *PT2_u0, *PT2_u1;
+static gdt_t *GDT;
+
+extern EFI_PHYSICAL_ADDRESS staging;
+
+static void (*trampoline)(uint32_t stack, void *copy_finish, uint32_t kernend,
+    uint32_t modulep, uint64_t *pagetable, struct gdtr *gdtr, uint64_t entry);
+
+extern void *amd64_tramp;
+extern uint32_t amd64_tramp_size;
+
+/*
+ * There is an ELF kernel and one or more ELF modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+	EFI_PHYSICAL_ADDRESS	ptr;
+	EFI_ALLOCATE_TYPE	type;
+	EFI_STATUS		err;
+	struct file_metadata	*md;
+	struct gdtr		*gdtr;
+	Elf_Ehdr 		*ehdr;
+	vm_offset_t		modulep, kernend, trampstack;
+	int i;
+
+	switch (copy_staging) {
+	case COPY_STAGING_ENABLE:
+		type = AllocateMaxAddress;
+		break;
+	case COPY_STAGING_DISABLE:
+		type = AllocateAnyPages;
+		break;
+	case COPY_STAGING_AUTO:
+		type = fp->f_kernphys_relocatable ?
+		    AllocateAnyPages : AllocateMaxAddress;
+		break;
+	}
+
+	if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+		return (EFTYPE);
+	ehdr = (Elf_Ehdr *)&(md->md_data);
+
+	/*
+	 * Make our temporary stack 32 bytes big, which is
+	 * a little more than we need.
+	 */
+	ptr = G(1);
+	err = BS->AllocatePages(type, EfiLoaderCode,
+	    EFI_SIZE_TO_PAGES(amd64_tramp_size + 32), &ptr);
+	if (EFI_ERROR(err)) {
+		printf("Unable to allocate trampoline\n");
+		return (ENOMEM);
+	}
+
+	trampoline = (void *)(uintptr_t)ptr;
+	trampstack = ptr + amd64_tramp_size + 32;
+	bcopy(&amd64_tramp, trampoline, amd64_tramp_size);
+
+	ptr = G(1);
+	err = BS->AllocatePages(type, EfiLoaderData,
+	    EFI_SIZE_TO_PAGES(sizeof(struct gdtr) + sizeof(uint64_t) * 2), &ptr);
+	if (EFI_ERROR(err)) {
+		printf("Unable to allocate GDT\n");
+		BS->FreePages((uintptr_t)trampoline, 1);
+		return (ENOMEM);
+	}
+	GDT = (gdt_t *)(uintptr_t)ptr;
+	GDT[1] = GDT_P | GDT_E | GDT_S | GDT_RW | GDT_L; /* CS */
+	GDT[0] = 0;
+	gdtr = (struct gdtr *)&GDT[2];
+	gdtr->size = sizeof(uint64_t) * 2 - 1;
+	gdtr->ptr = (uintptr_t)GDT;
+
+	if (type == AllocateMaxAddress) {
+		/* Copy staging enabled */
+
+		ptr = G(1);
+		err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
+		    EFI_SIZE_TO_PAGES(512 * 3 * sizeof(uint64_t)), &ptr);
+		if (EFI_ERROR(err)) {
+			printf("Unable to allocate trampoline page table\n");
+			BS->FreePages((uintptr_t)trampoline, 1);
+			BS->FreePages((uintptr_t)GDT, 1);
+			return (ENOMEM);
+		}
+		PT4 = (p4_entry_t *)(uintptr_t)ptr;
+
+		PT3 = &PT4[512];
+		PT2 = &PT3[512];
+
+		/*
+		 * This is kinda brutal, but every single 1GB VM
+		 * memory segment points to the same first 1GB of
+		 * physical memory.  But it is more than adequate.
+		 */
+		for (i = 0; i < 512; i++) {
+			/*
+			 * Each slot of the L4 pages points to the
+			 * same L3 page.
+			 */
+			PT4[i] = (uintptr_t)PT3 | PG_V | PG_RW;
+
+			/*
+			 * Each slot of the L3 pages points to the
+			 * same L2 page.
+			 */
+			PT3[i] = (uintptr_t)PT2 | PG_V | PG_RW;
+
+			/*
+			 * The L2 page slots are mapped with 2MB pages for 1GB.
+			 */
+			PT2[i] = (i * M(2)) | PG_V | PG_RW | PG_PS;
+		}
+	} else {
+		err = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+		    EFI_SIZE_TO_PAGES(512 * 9 * sizeof(uint64_t)), &ptr);
+		if (EFI_ERROR(err)) {
+			printf("Unable to allocate trampoline page table\n");
+			BS->FreePages((uintptr_t)trampoline, 1);
+			BS->FreePages((uintptr_t)GDT, 1);
+			return (ENOMEM);
+		}
+		PT4 = (p4_entry_t *)(uintptr_t)ptr;
+
+		PT3_l = &PT4[512];
+		PT3_u = &PT3_l[512];
+		PT2_l0 = &PT3_u[512];
+		PT2_l1 = &PT2_l0[512];
+		PT2_l2 = &PT2_l1[512];
+		PT2_l3 = &PT2_l2[512];
+		PT2_u0 = &PT2_l3[512];
+		PT2_u1 = &PT2_u0[512];
+
+		/* 1:1 mapping of lower 4G */
+		PT4[0] = (uintptr_t)PT3_l | PG_V | PG_RW;
+		PT3_l[0] = (uintptr_t)PT2_l0 | PG_V | PG_RW;
+		PT3_l[1] = (uintptr_t)PT2_l1 | PG_V | PG_RW;
+		PT3_l[2] = (uintptr_t)PT2_l2 | PG_V | PG_RW;
+		PT3_l[3] = (uintptr_t)PT2_l3 | PG_V | PG_RW;
+		for (i = 0; i < 2048; i++) {
+			PT2_l0[i] = ((p2_entry_t)i * M(2)) | PG_V | PG_RW | PG_PS;
+		}
+
+		/* mapping of kernel 2G below top */
+		PT4[511] = (uintptr_t)PT3_u | PG_V | PG_RW;
+		PT3_u[511] = (uintptr_t)PT2_u1 | PG_V | PG_RW;
+		PT3_u[510] = (uintptr_t)PT2_u0 | PG_V | PG_RW;
+		/* compat mapping of phys @0 */
+		PT2_u0[0] = PG_PS | PG_V | PG_RW;
+		/* this maps past staging area */
+		for (i = 1; i < 1024; i++) {
+			PT2_u0[i] = (staging + (i - 1) * M(2))
+			| PG_V | PG_RW | PG_PS;
+		}
+	}
+
+	printf(
+	    "staging %#llx (%scopying) tramp %p PT4 %p GDT %p\n"
+	    "Start @ %#llx ...\n", staging,
+	    type == AllocateMaxAddress ? "" : "not ", trampoline, PT4, GDT,
+	    ehdr->e_entry
+	);
+
+	efi_time_fini();
+	err = bi_load(fp->f_args, &modulep, &kernend, true);
+	if (err != 0) {
+		efi_time_init();
+		return (err);
+	}
+
+	dev_cleanup();
+
+	trampoline(trampstack, type == AllocateMaxAddress ? efi_copy_finish :
+	    efi_copy_finish_nop, kernend, modulep, PT4, gdtr, ehdr->e_entry);
+
+	panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+	return (EFTYPE);
+}
diff --git a/stand/efi/loader/arch/i386/i386.ldscript b/stand/efi/loader/arch/i386/i386.ldscript
new file mode 100644
index 000000000000..dcbbf1b6d609
--- /dev/null
+++ b/stand/efi/loader/arch/i386/i386.ldscript
@@ -0,0 +1,55 @@
+OUTPUT_FORMAT("elf32-i386-freebsd", "elf32-i386-freebsd", "elf32-i386-freebsd")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0;
+  ImageBase = .;
+  . = SIZEOF_HEADERS;
+  . = ALIGN(4096);
+  .text		: {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em. */
+    *(.gnu.warning)
+    *(.plt)
+  } =0xCCCCCCCC
+  . = ALIGN(4096);
+  .data		: {
+    *(.rodata .rodata.* .gnu.linkonce.r.*)
+    *(.rodata1)
+    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+    *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
+    *(.opd)
+    *(.data .data.* .gnu.linkonce.d.*)
+    *(.data1)
+    *(.plabel)
+    *(.dynbss)
+    *(.bss .bss.* .gnu.linkonce.b.*)
+    *(COMMON)
+  }
+  . = ALIGN(4096);
+  __gp = .;
+  .sdata	: {
+    *(.got.plt .got)
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+    *(dynsbss)
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+  }
+  . = ALIGN(4096);
+  .dynamic	: { *(.dynamic) }
+  . = ALIGN(4096);
+  .rel.dyn	: {
+    *(.rel.*)
+    *(.relset_*)
+  }
+  . = ALIGN(4096);
+  .reloc	: { *(.reloc) }
+  . = ALIGN(4096);
+  .hash		: { *(.hash) }
+  . = ALIGN(4096);
+  .dynsym	: { *(.dynsym) }
+  . = ALIGN(4096);
+  .dynstr	: { *(.dynstr) }
+}
diff --git a/stand/efi/loader/arch/i386/setup.c b/stand/efi/loader/arch/i386/setup.c
new file mode 100644
index 000000000000..5d15e499ddb3
--- /dev/null
+++ b/stand/efi/loader/arch/i386/setup.c
@@ -0,0 +1,51 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Ahmad Khalifa <ahmadkhalifa570@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <machine/specialreg.h>
+
+/*
+ * Check for long mode then call efi_main
+ */
+EFI_STATUS
+setup(EFI_HANDLE IH, EFI_SYSTEM_TABLE *ST) {
+	u_int edx;
+
+	asm("cpuid" : "=d"(edx) : "a"(0x80000001) : "ebx", "ecx");
+	if ((edx & AMDID_LM) == 0) {
+		ST->ConOut->OutputString(ST->ConOut, (CHAR16 *)
+		    L"This CPU doesn't support long mode.\r\n"
+		    L"Unable to proceed.\r\n");
+		ST->BootServices->Exit(IH, EFI_UNSUPPORTED, 0, NULL);
+	}
+
+	return (efi_main(IH, ST));
+}
diff --git a/stand/efi/loader/arch/i386/start.S b/stand/efi/loader/arch/i386/start.S
new file mode 100644
index 000000000000..2754c81e509c
--- /dev/null
+++ b/stand/efi/loader/arch/i386/start.S
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (C) 1999 Hewlett-Packard Co.
+ *     Contributed by David Mosberger <davidm@hpl.hp.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hewlett-Packard Co. nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * crt0-efi-ia32.S - x86 EFI startup code.
+ */
+
+	.text
+	.align 4
+
+	.globl _start
+_start:
+	pushl %ebp
+	movl %esp, %ebp
+
+	pushl 12(%ebp)
+	pushl  8(%ebp)
+
+	call 0f
+0:	popl %eax
+	movl %eax, %ebx
+
+	addl $ImageBase-0b, %eax
+	addl $_DYNAMIC-0b, %ebx
+
+	pushl %ebx
+	pushl %eax
+	call self_reloc
+	popl %ebx
+	popl %ebx
+
+	call setup
+
+.exit:
+	leave
+	ret
+
+	/*
+	 * hand-craft a dummy .reloc section so EFI knows it's a relocatable
+	 * executable:
+	 */
+
+	.data
+	.section .reloc, "a"
+	.long   0
+	.long   10
+	.word   0
diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c
index 0a53422142e2..5afb4c78353e 100644
--- a/stand/efi/loader/bootinfo.c
+++ b/stand/efi/loader/bootinfo.c
@@ -185,7 +185,7 @@ bi_load_efi_data(struct preloaded_file *kfp, bool exit_bs)
 	struct efi_map_header *efihdr;
 	bool do_vmap;
 
-#if defined(__amd64__) || defined(__aarch64__)
+#if defined(__amd64__) || defined(__aarch64__) || defined(__i386__)
 	struct efi_fb efifb;
 
 	efifb.fb_addr = gfx_state.tg_fb.fb_addr;
@@ -339,7 +339,16 @@ bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp, bool exit_bs)
 	vm_offset_t size;
 	char *rootdevname;
 	int howto;
+#ifdef __i386__
+	/*
+	 * The 32-bit UEFI loader is used to
+	 * boot the 64-bit kernel on machines
+	 * that support it.
+	 */
+	bool is64 = true;
+#else
 	bool is64 = sizeof(long) == 8;
+#endif
 #if defined(LOADER_FDT_SUPPORT)
 	vm_offset_t dtbp;
 	int dtb_size;
@@ -438,7 +447,7 @@ bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp, bool exit_bs)
 	module = *modulep;
 	file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof(module), &module);
 #endif
-#ifdef EFI
+#if defined(EFI) && !defined(__i386__)
 	file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(ST), &ST);
 #endif
 #ifdef LOADER_GELI_SUPPORT
diff --git a/stand/efi/loader/conf.c b/stand/efi/loader/conf.c
index 3bc74ea6354c..0a0476873fe1 100644
--- a/stand/efi/loader/conf.c
+++ b/stand/efi/loader/conf.c
@@ -82,7 +82,7 @@ extern struct console eficom;
 /* Hack for backward compatibility -- but only for a while */
 extern struct console comconsole;
 #endif
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
 extern struct console comconsole;
 extern struct console nullconsole;
 extern struct console spinconsole;
@@ -94,7 +94,7 @@ struct console *consoles[] = {
 #if defined(__aarch64__) && __FreeBSD_version < 1500000
 	&comconsole,
 #endif
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
 	&comconsole,
 	&nullconsole,
 	&spinconsole,
diff --git a/stand/efi/loader/copy.c b/stand/efi/loader/copy.c
index a0aea9823632..d34acea4e876 100644
--- a/stand/efi/loader/copy.c
+++ b/stand/efi/loader/copy.c
@@ -182,13 +182,13 @@ out:
 #endif
 
 #if defined(__aarch64__) || defined(__amd64__) || defined(__arm__) || \
-    defined(__riscv)
+    defined(__riscv) || defined(__i386__)
 #define	EFI_STAGING_2M_ALIGN	1
 #else
 #define	EFI_STAGING_2M_ALIGN	0
 #endif
 
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
 #define	EFI_STAGING_SLOP	M(8)
 #else
 #define	EFI_STAGING_SLOP	0
@@ -209,7 +209,7 @@ efi_copy_free(void)
 	stage_offset = 0;
 }
 
-#ifdef __amd64__
+#if defined(__amd64__) || defined(__i386__)
 int copy_staging = COPY_STAGING_AUTO;
 
 static int
@@ -281,7 +281,7 @@ command_staging_slop(int argc, char *argv[])
 COMMAND_SET(staging_slop, "staging_slop", "set staging slop",
     command_staging_slop);
 
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
 /*
  * The staging area must reside in the first 1GB or 4GB physical
  * memory: see elf64_exec() in
@@ -320,7 +320,8 @@ efi_copy_init(void)
 	 */
 	if (running_on_hyperv())
 		efi_verify_staging_size(&nr_pages);
-
+#endif
+#if defined(__amd64__) || defined(__i386__)
 	staging = get_staging_max();
 #endif
 	status = BS->AllocatePages(EFI_ALLOC_METHOD, EfiLoaderCode,
@@ -380,9 +381,10 @@ efi_check_space(vm_offset_t end)
 	end += staging_slop;
 
 	nr_pages = EFI_SIZE_TO_PAGES(end - staging_end);
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
 	/*
-	 * amd64 needs all memory to be allocated under the 1G or 4G boundary.
+	 * The amd64 kernel needs all memory to be allocated under the 1G or
+	 * 4G boundary.
 	 */
 	if (end > get_staging_max())
 		goto before_staging;
@@ -427,7 +429,7 @@ expand:
 #if EFI_STAGING_2M_ALIGN
 	nr_pages += M(2) / EFI_PAGE_SIZE;
 #endif
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
 	new_base = get_staging_max();
 #endif
 	status = BS->AllocatePages(EFI_ALLOC_METHOD, EfiLoaderCode,
diff --git a/stand/efi/loader/loader_efi.h b/stand/efi/loader/loader_efi.h
index 249860aa5e91..c9dfefcd4c74 100644
--- a/stand/efi/loader/loader_efi.h
+++ b/stand/efi/loader/loader_efi.h
@@ -32,7 +32,7 @@
 #include <readin.h>
 #include <efi.h>
 
-#ifdef __amd64__
+#if defined(__amd64__) || defined(__i386__)
 enum {
 	COPY_STAGING_ENABLE,
 	COPY_STAGING_DISABLE,
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
index 69fee3128569..790d5c0ce1ad 100644
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -964,7 +964,7 @@ main(int argc, CHAR16 *argv[])
 	archsw.arch_getdev = efi_getdev;
 	archsw.arch_copyin = efi_copyin;
 	archsw.arch_copyout = efi_copyout;
-#ifdef __amd64__
+#if defined(__amd64__) || defined(__i386__)
 	archsw.arch_hypervisor = x86_hypervisor;
 #endif
 	archsw.arch_readin = efi_readin;
diff --git a/stand/efi/loader_ia32/Makefile b/stand/efi/loader_ia32/Makefile
new file mode 100644
index 000000000000..003c75f43948
--- /dev/null
+++ b/stand/efi/loader_ia32/Makefile
@@ -0,0 +1,6 @@
+DO32=1
+INSTALL_LOADER_HELP_FILE=no
+
+NEWVERSWHAT?=	"EFI loader" amd64-ia32
+
+.include "../loader/Makefile"



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