aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAhmad Khalifa <ahmadkhalifa570@gmail.com>2024-05-14 19:40:06 +0000
committerWarner Losh <imp@FreeBSD.org>2024-09-20 14:45:08 +0000
commitf8ca5d45c3c1829759ecd87cb95d53e5ab7d0811 (patch)
tree179353400abf9573a274df964c5041303d984e75
parent6818ff7797c6a9ab104e5d7e91d4e422874bf50d (diff)
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
-rw-r--r--stand/common/bootstrap.h2
-rw-r--r--stand/common/load_elf.c4
-rw-r--r--stand/efi/Makefile1
-rw-r--r--stand/efi/Makefile.inc4
-rw-r--r--stand/efi/loader/Makefile24
-rw-r--r--stand/efi/loader/arch/i386/Makefile.inc9
-rw-r--r--stand/efi/loader/arch/i386/amd64_tramp.S100
-rw-r--r--stand/efi/loader/arch/i386/elf64_freebsd.c274
-rw-r--r--stand/efi/loader/arch/i386/i386.ldscript55
-rw-r--r--stand/efi/loader/arch/i386/setup.c51
-rw-r--r--stand/efi/loader/arch/i386/start.S75
-rw-r--r--stand/efi/loader/bootinfo.c13
-rw-r--r--stand/efi/loader/conf.c4
-rw-r--r--stand/efi/loader/copy.c18
-rw-r--r--stand/efi/loader/loader_efi.h2
-rw-r--r--stand/efi/loader/main.c2
-rw-r--r--stand/efi/loader_ia32/Makefile6
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"