[RFC 3/7] efi/emulator: Initial rountines to emulate EFI boot time service
Pingfan Liu
piliu at redhat.com
Fri Jul 19 07:55:13 PDT 2024
On Thu, Jul 18, 2024 at 4:58 PM Pingfan Liu <piliu at redhat.com> wrote:
>
> From: Pingfan Liu <kernelfans at gmail.com>
>
> efi emulator aims to serve the kexec if the kernel wrapped by efistub.
> It is paired with efistub, so only implements the minimus set of EFI boot
> service which merely enough boots up efistub.
>
> To simplify the code, the task such as the building of page table etc is
> shift to the first kernel as more as possible. (This part is implement
> in the later patch in this series)
>
> To do:
> -1. a simple memory allocator
> -2. a few extra efi boot service.
>
> Signed-off-by: Pingfan Liu <piliu at redhat.com>
> Cc: Ard Biesheuvel <ardb at kernel.org>
> Cc: Jan Hendrik Farr <kernel at jfarr.cc>
> Cc: Philipp Rudo <prudo at redhat.com>
> Cc: Lennart Poettering <mzxreary at 0pointer.de>
> Cc: Jarkko Sakkinen <jarkko at kernel.org>
> Cc: Baoquan He <bhe at redhat.com>
> Cc: Dave Young <dyoung at redhat.com>
> Cc: Mark Rutland <mark.rutland at arm.com>
> Cc: Will Deacon <will at kernel.org>
> Cc: Catalin Marinas <catalin.marinas at arm.com>
> To: linux-arm-kernel at lists.infradead.org
> To: kexec at lists.infradead.org
> To: linux-efi at vger.kernel.org
> ---
> drivers/firmware/efi/Makefile | 1 +
> drivers/firmware/efi/efi_emulator/Makefile | 98 +++++
> .../firmware/efi/efi_emulator/amba-pl011.c | 80 ++++
> .../efi_emulator/arm64_emulator_service.lds | 45 ++
> .../firmware/efi/efi_emulator/config_table.c | 23 ++
> drivers/firmware/efi/efi_emulator/core.c | 211 ++++++++++
> drivers/firmware/efi/efi_emulator/earlycon.h | 19 +
> .../firmware/efi/efi_emulator/efi_emulator.S | 12 +
> drivers/firmware/efi/efi_emulator/emulator.h | 66 +++
> drivers/firmware/efi/efi_emulator/entry.c | 57 +++
> drivers/firmware/efi/efi_emulator/head.S | 10 +
> drivers/firmware/efi/efi_emulator/initrd.c | 15 +
> drivers/firmware/efi/efi_emulator/lib.c | 73 ++++
> drivers/firmware/efi/efi_emulator/memory.c | 27 ++
> .../firmware/efi/efi_emulator/memory_api.c | 73 ++++
> drivers/firmware/efi/efi_emulator/misc.c | 76 ++++
> drivers/firmware/efi/efi_emulator/pe_loader.c | 124 ++++++
> drivers/firmware/efi/efi_emulator/printf.c | 389 ++++++++++++++++++
> .../efi/efi_emulator/runtime_service.c | 28 ++
> include/linux/efi_emulator.h | 45 ++
> include/linux/kexec.h | 2 +
> 21 files changed, 1474 insertions(+)
> create mode 100644 drivers/firmware/efi/efi_emulator/Makefile
> create mode 100644 drivers/firmware/efi/efi_emulator/amba-pl011.c
> create mode 100644 drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds
> create mode 100644 drivers/firmware/efi/efi_emulator/config_table.c
> create mode 100644 drivers/firmware/efi/efi_emulator/core.c
> create mode 100644 drivers/firmware/efi/efi_emulator/earlycon.h
> create mode 100644 drivers/firmware/efi/efi_emulator/efi_emulator.S
> create mode 100644 drivers/firmware/efi/efi_emulator/emulator.h
> create mode 100644 drivers/firmware/efi/efi_emulator/entry.c
> create mode 100644 drivers/firmware/efi/efi_emulator/head.S
> create mode 100644 drivers/firmware/efi/efi_emulator/initrd.c
> create mode 100644 drivers/firmware/efi/efi_emulator/lib.c
> create mode 100644 drivers/firmware/efi/efi_emulator/memory.c
> create mode 100644 drivers/firmware/efi/efi_emulator/memory_api.c
> create mode 100644 drivers/firmware/efi/efi_emulator/misc.c
> create mode 100644 drivers/firmware/efi/efi_emulator/pe_loader.c
> create mode 100644 drivers/firmware/efi/efi_emulator/printf.c
> create mode 100644 drivers/firmware/efi/efi_emulator/runtime_service.c
> create mode 100644 include/linux/efi_emulator.h
>
> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
> index a2d0009560d0f..eb2a5d864f416 100644
> --- a/drivers/firmware/efi/Makefile
> +++ b/drivers/firmware/efi/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
> obj-$(CONFIG_UEFI_CPER) += cper.o cper_cxl.o
> obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
> subdir-$(CONFIG_EFI_STUB) += libstub
> +obj-y += efi_emulator/
> obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
> obj-$(CONFIG_EFI_TEST) += test/
> obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o
> diff --git a/drivers/firmware/efi/efi_emulator/Makefile b/drivers/firmware/efi/efi_emulator/Makefile
> new file mode 100644
> index 0000000000000..e37472004a2b5
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/Makefile
> @@ -0,0 +1,98 @@
> +# non-x86 reuses KBUILD_CFLAGS, x86 does not
> +cflags-y := $(KBUILD_CFLAGS)
> +
> +cflags-$(CONFIG_X86_32) := -march=i386
> +cflags-$(CONFIG_X86_64) := -mcmodel=small
> +cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \
> + -fno-strict-aliasing -mno-red-zone \
> + -mno-mmx -mno-sse -fshort-wchar \
> + -Wno-pointer-sign \
> + $(call cc-disable-warning, address-of-packed-member) \
> + $(call cc-disable-warning, gnu) \
> + -fno-asynchronous-unwind-tables \
> + $(CLANG_FLAGS)
> +
> +# arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly
> +# disable the stackleak plugin
> +cflags-$(CONFIG_ARM64) += $(DISABLE_STACKLEAK_PLUGIN) \
> + -fno-unwind-tables -fno-asynchronous-unwind-tables
> +cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \
> + -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \
> + -DEFI_HAVE_STRCMP -fno-builtin \
> + $(call cc-option,-mno-single-pic-base)
> +cflags-$(CONFIG_RISCV) += -DNO_ALTERNATIVE -mno-relax
> +cflags-$(CONFIG_LOONGARCH) +=
> +
> +cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt
> +
> +cflags-y += -I drivers/firmware/efi/libstub
> +
> +KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(cflags-y)) \
> + -Os -DDISABLE_BRANCH_PROFILING \
> + -D__NO_FORTIFY \
> + -ffreestanding \
> + -fno-stack-protector \
> + $(call cc-option,-fno-addrsig) \
> + -D__DISABLE_EXPORTS
> +
> +#
> +# struct randomization only makes sense for Linux internal types, which the EFI
> +# stub code never touches, so let's turn off struct randomization for the stub
> +# altogether
> +#
> +KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS), $(KBUILD_CFLAGS))
> +
> +# remove SCS flags from all objects in this directory
> +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_SCS), $(KBUILD_CFLAGS))
> +# disable CFI
> +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_CFI), $(KBUILD_CFLAGS))
> +# disable LTO
> +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO), $(KBUILD_CFLAGS))
> +
> +GCOV_PROFILE := n
> +# Sanitizer runtimes are unavailable and cannot be linked here.
> +KASAN_SANITIZE := n
> +KCSAN_SANITIZE := n
> +KMSAN_SANITIZE := n
> +UBSAN_SANITIZE := n
> +OBJECT_FILES_NON_STANDARD := y
> +
> +# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
> +KCOV_INSTRUMENT := n
> +
> +OBJECT_FILES_NON_STANDARD := y
> +emulator-y := head.o entry.o \
> + core.o pe_loader.o misc.o memory.o memory_api.o runtime_service.o config_table.o \
> + lib.o printf.o \
> + amba-pl011.o
> +obj-y := efi_emulator.o
> +
> +
> +EMULATOR_OBJS = $(addprefix $(obj)/,$(emulator-y))
> +
> +quiet_cmd_ar_emulator = PAD $@
> + cmd_ar_emulator = $(AR) rcSTP $@ $^
> +
> +$(obj)/emulator.a: $(EMULATOR_OBJS)
> + $(call if_changed,ar_emulator)
> +
> +
> +quiet_cmd_link_emulator = PAD $@
> + cmd_link_emulator = ld -z norelro -z noexecstack -shared --no-undefined -X -Bsymbolic -z notext --emit-relocs --no-apply-dynamic-relocs \
> + -T $(srctree)/drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds \
> + --whole-archive $< --no-whole-archive -o $@
> +
> +
> +$(obj)/emulator.ro: $(obj)/emulator.a FORCE
> + $(call if_changed,link_emulator)
> +
> +
> +$(obj)/emulator.raw: $(obj)/emulator.ro FORCE
> + objcopy -O binary -R .note -R .note.gnu.build-id -R .comment -g $< $@
> +
> +$(obj)/efi_emulator.o: $(obj)/emulator.raw
> +
> +
> +targets += $(emulator-y)
> +
> +
> diff --git a/drivers/firmware/efi/efi_emulator/amba-pl011.c b/drivers/firmware/efi/efi_emulator/amba-pl011.c
> new file mode 100644
> index 0000000000000..334b25e177081
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/amba-pl011.c
> @@ -0,0 +1,80 @@
> +
> +#include <linux/stdarg.h>
> +#include <linux/amba/serial.h>
> +#include "earlycon.h"
> +
> +#define SERIAL_IO_MEM32 3
> +#define UPIO_MEM32 SERIAL_IO_MEM32
> +
> +struct uart_port {
> + unsigned long iobase; /* in/out[bwl] */
> + unsigned char __iomem *membase; /* read/write[bwl] */
> + unsigned char iotype; /* io access style */
> +};
> +
> +static struct uart_port pl011_port;
> +
> +static void pl011_putc(struct uart_port *port, unsigned char c)
> +{
> + while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF)
> + cpu_relax();
> + if (port->iotype == UPIO_MEM32)
> + writel(c, port->membase + UART01x_DR);
> + else
> + writeb(c, port->membase + UART01x_DR);
> + while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY)
> + cpu_relax();
> +}
> +
> +static int pl011_put_str(const char *str, void *data)
> +{
> + char *p = (char *)str;
> + struct uart_port *port = (struct uart_port *)data;
> +
> + for (; *p != '\0'; p++)
> + pl011_putc(port, *p);
> +
> + return (p - str);
> +}
> +
> +static void pl011_write(struct uart_port *port, unsigned int reg, unsigned int val)
> +{
> + void __iomem *addr = port->membase + reg;
> +
> + if (port->iotype == UPIO_MEM32)
> + writel_relaxed(val, addr);
> + else
> + writew_relaxed(val, addr);
> +}
> +
> +static bool pl011_match(struct efi_emulator_param *param, const char *name)
> +{
> + struct uart_port *port = &pl011_port;
> +
> + if (strcmp(param->earlycon_name, name))
> + return false;
> +
> + port->iotype = UPIO_MEM32;
> + port->membase = (unsigned char *)param->earlycon_reg_base;
> + return true;
> +}
> +
> +static void pl011_reset(void *data)
> +{
> + struct uart_port *port = data;
> +
> + /* disable DMA */
> + pl011_write(port, UART011_DMACR, 0);
> + /* disable interrupt */
> + pl011_write(port, UART011_IMSC, 0);
> + /* Skip: set clk rate */
> + /* Now, pl011 can be used in poll mode */
> +}
> +
> +struct earlycon pl011 = {
> + .match = pl011_match,
> + .reset = pl011_reset,
> + .put_str = pl011_put_str,
> + .data = &pl011_port,
> + .name = "amba-pl011",
> +};
> diff --git a/drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds b/drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds
> new file mode 100644
> index 0000000000000..82d7659346af5
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +ENTRY(emulator_entry)
> +EMULATOR_BASE_ADDR = 0x0;
> +
> +SECTIONS
> +{
> + . = EMULATOR_BASE_ADDR;
> + .head : ALIGN(4096) {
> + *(.head.text)
> + }
> +
> + .text : {
> + *(.text* .init.text*)
> + }
> +
> + .rodata : ALIGN(8) {
> + *(.rodata* .init.rodata* .srodata*)
> +
> + _etext = ALIGN(4096);
> + . = _etext;
> + }
> +
> + .rela.dyn : ALIGN(4096) {
> + _rela_start = .;
> + *(.rela .rela*)
> + _rela_end = .;
> + }
> +
> + .data : ALIGN(4096) {
> + *(.data* .init.data*)
> + _edata = ALIGN(512);
> + . = _edata;
> + }
> +
> + .bss : {
> + *(.bss* .init.bss*)
> + _end = ALIGN(512);
> + . = _end;
> + }
> +
> + /DISCARD/ : {
> + *(.modinfo .init.modinfo)
> + }
> +}
> diff --git a/drivers/firmware/efi/efi_emulator/config_table.c b/drivers/firmware/efi/efi_emulator/config_table.c
> new file mode 100644
> index 0000000000000..3efe76cbd86fd
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/config_table.c
> @@ -0,0 +1,23 @@
> +
> +#include "emulator.h"
> +
> +
> +static efi_status_t conjoin_memreserve_table(void *table, efi_config_table_t *t)
> +{
> + struct linux_efi_memreserve *prev, *next;
> +
> + prev = (struct linux_efi_memreserve *)t->table;
> + next = (struct linux_efi_memreserve *)table;
> +
> + prev->next = (phys_addr_t)next;
> + next->next = 0;
> + return EFI_SUCCESS;
> +}
> +
> +efi_status_t conjoin_table(efi_guid_t *uuid, void *table, efi_config_table_t *t)
> +{
> + if (!efi_guidcmp(t->guid, LINUX_EFI_MEMRESERVE_TABLE_GUID))
> + return conjoin_memreserve_table(table, t);
> +
> + return EFI_OUT_OF_RESOURCES;
> +}
> diff --git a/drivers/firmware/efi/efi_emulator/core.c b/drivers/firmware/efi/efi_emulator/core.c
> new file mode 100644
> index 0000000000000..29f3671a2db26
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/core.c
> @@ -0,0 +1,211 @@
> +#include <linux/efi.h>
> +#include <asm/efi.h>
> +
> +#include "emulator.h"
> +
> +int emulator_initialize(void);
> +
> +struct efi_emulator_param *emulator_param;
> +
> +static efi_loaded_image_t loaded_image;
> +static bool print_enabled;
> +
> +/* The 1st kernel convert cmdline to utf16 and pass to emulator */
> +static efi_status_t handle_protocol_loaded_image(efi_handle_t h, void **data)
> +{
> + loaded_image.load_options = emulator_param->cmdline;
> + loaded_image.load_options_size = emulator_param->sz_in_byte;
> + /* loaded address */
> + loaded_image.image_base = 0x0;
Here, image_base should point to the PE file loaded address. So the
pe_memory_locate_sections() in systemd-stub can locate the in-memory
UKI image and parse each section.
And in the emulator, all things connect with UEFI, it does not
perceive the UKI image internal layout.
Thanks,
Pingfan
> +
> + *data = &loaded_image;
> + return EFI_SUCCESS;
> +
> +}
> +
> +
> +static efi_status_t __efiapi emulator_handle_protocol(efi_handle_t h,
> + efi_guid_t *uuid, void **data)
> +{
> + if (!efi_guidcmp(*uuid, LOADED_IMAGE_PROTOCOL_GUID))
> + return handle_protocol_loaded_image(h, data);
> +
> + return EFI_UNSUPPORTED;
> +}
> +
> +//The LocateProtocol() function finds the first device handle that support Protocol, and returns a
> +// pointer to the protocol interface from that handle in Interface. If no protocol instances are found, then Interface is set to NULL.
> +static efi_status_t __efiapi emulator_locate_protocol(efi_guid_t *uuid,
> + void *registration, void **interface)
> +{
> + if (!efi_guidcmp(*uuid, EFI_TCG2_PROTOCOL_GUID)) {
> + *interface = &emulator_tcg2;
> + return EFI_SUCCESS;
> + } else if (!efi_guidcmp(*uuid, EFI_CC_MEASUREMENT_PROTOCOL_GUID)) {
> + *interface = &emulator_cc;
> + return EFI_SUCCESS;
> + } else if (!efi_guidcmp(*uuid, EFI_RNG_PROTOCOL_GUID)) {
> + *interface = &emulator_rng;
> + return EFI_SUCCESS;
> + }
> +
> + return EFI_UNSUPPORTED;
> +}
> +
> +// 2do
> +static efi_status_t __efiapi emulator_allocate_pages(int alloc_type, int mem_type,
> + unsigned long nr_pages, efi_physical_addr_t *addr)
> +{
> + return __emulator_allocate_pages(alloc_type, mem_type, nr_pages, addr);
> +}
> +
> +// 2do
> +static efi_status_t __efiapi emulator_free_pages(efi_physical_addr_t addr,
> + unsigned long nr_4KB)
> +{
> + return EFI_SUCCESS;
> +
> +}
> +
> +static efi_status_t __efiapi emulator_allocate_pool(int mem_type, unsigned long sz,
> + void **pool)
> +{
> + return __emulator_allocate_pool(mem_type, sz, pool);
> +
> +}
> +
> +static efi_status_t __efiapi emulator_free_pool(void *pool)
> +{
> + return EFI_SUCCESS;
> +
> +}
> +
> +/* memmove() alias as memcpy() */
> +static void __efiapi emulator_copy_mem(void *dest, const void *src, unsigned long count)
> +{
> + char *tmp;
> + const char *s;
> +
> + if (dest <= src) {
> + tmp = dest;
> + s = src;
> + while (count--)
> + *tmp++ = *s++;
> + } else {
> + tmp = dest;
> + tmp += count;
> + s = src;
> + s += count;
> + while (count--)
> + *--tmp = *--s;
> + }
> +
> +}
> +
> +static void __efiapi emulator_set_mem(void *dst, unsigned long cnt, unsigned char val)
> +{
> + unsigned char *dst_ptr = (char *)dst;
> + unsigned long i;
> +
> + for (i = 0; i < cnt; i++)
> + dst_ptr[i] = val;
> +}
> +
> +static efi_status_t __efiapi emulator_install_configuration_table(efi_guid_t *uuid,
> + void *table)
> +{
> + efi_config_table_t *t = (efi_config_table_t *)systabs.tables;
> + int i;
> +
> + for (i = 0; i < systabs.nr_tables; i++, t++) {
> + if (!efi_guidcmp(t->guid, *uuid))
> + return conjoin_table(uuid, table, t);
> + }
> + t->guid = *uuid;
> + t->table = table;
> + systabs.nr_tables++;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/*
> + * As the final stage, destroy the boottime context, e.g. release the memory
> + * occupied by some data struct.
> + */
> +static efi_status_t __efiapi emulator_exit_boot_services(efi_handle_t handle,
> + unsigned long map_key)
> +{
> + return EFI_SUCCESS;
> +}
> +
> +static efi_boot_services_t bt_services = {
> + .handle_protocol = emulator_handle_protocol,
> + .locate_protocol = emulator_locate_protocol,
> + .allocate_pool = emulator_allocate_pool,
> + .free_pool = emulator_free_pool,
> + .allocate_pages = emulator_allocate_pages,
> + .free_pages = emulator_free_pages,
> + .copy_mem = emulator_copy_mem,
> + .set_mem = emulator_set_mem,
> + .get_memory_map = emulator_get_memory_map,
> + .install_configuration_table = emulator_install_configuration_table,
> + .exit_boot_services = emulator_exit_boot_services,
> +};
> +
> +/* UCS-2 (Universal Coded Character Set) */
> +static efi_status_t __efiapi output_string(efi_simple_text_output_protocol_t * simple,
> + efi_char16_t *str)
> +{
> + if (print_enabled)
> + print_ucs2_string(str);
> + return EFI_SUCCESS;
> +}
> +
> +static efi_simple_text_output_protocol_t text_out = {
> + .output_string = output_string,
> +};
> +
> +efi_system_table_t systabs = {
> + .hdr = {
> + .signature = EFI_SYSTEM_TABLE_SIGNATURE,
> + },
> +
> + .con_out = &text_out,
> + .runtime = NULL,
> + .boottime = &bt_services,
> + .nr_tables = 0,
> + .tables = 0,
> +
> +};
> +
> +static efi_rt_properties_table_t rt_support = {
> + .runtime_services_supported = 0,
> +};
> +
> +int initialize_emulator_service(struct efi_emulator_param *param)
> +{
> +
> + efi_config_table_t *tables;
> + unsigned int i;
> +
> + printf("initialize_emulator_service, dtb=0x%lx, mempool_start=0x%lx, end:0x%lx\n",
> + param->dtb, param->mempool_start, param->mempool_start + param->mempool_sz);
> + emulator_param = param;
> + print_enabled = param->print_enabled;
> + i = param->rt_info.systab_nr_tables;
> + systabs.tables = (unsigned long)¶m->rt_info.systab_tables;
> + tables = param->rt_info.systab_tables;
> + tables[i].guid = DEVICE_TREE_GUID;
> + tables[i].table = (void *)param->dtb;
> + i++;
> + if (!param->noefi_boot) {
> + rt_support.runtime_services_supported = param->rt_info.runtime_supported_mask;
> + }
> + tables[i].guid = EFI_RT_PROPERTIES_TABLE_GUID;
> + tables[i].table = (void *)&rt_support;
> + i++;
> + systabs.nr_tables = i;
> +
> + systabs.runtime = (efi_runtime_services_t *)param->rt_info.runtime;
> + return 0;
> +}
> diff --git a/drivers/firmware/efi/efi_emulator/earlycon.h b/drivers/firmware/efi/efi_emulator/earlycon.h
> new file mode 100644
> index 0000000000000..189af549d5af2
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/earlycon.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#include <linux/efi_emulator.h>
> +#include <asm/processor.h>
> +#include <asm/io.h>
> +#include <asm-generic/io.h>
> +
> +struct earlycon {
> + bool (*match)(struct efi_emulator_param *param, const char *name);
> + int (*put_str)(const char *str, void *data);
> + void (*reset)(void *data);
> + void *data;
> + const char *name;
> +};
> +
> +extern struct earlycon pl011;
> +
> +extern int pl011_puts(const char *str);
> +void setup_earlycon(struct efi_emulator_param *param);
> diff --git a/drivers/firmware/efi/efi_emulator/efi_emulator.S b/drivers/firmware/efi/efi_emulator/efi_emulator.S
> new file mode 100644
> index 0000000000000..fb52593ba3b2e
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/efi_emulator.S
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> + .section .rodata, "a"
> +
> + .align 8
> +_efi_emulator_start:
> + .globl _efi_emulator_start
> + .incbin "drivers/firmware/efi/efi_emulator/emulator.raw"
> +
> + .align 8
> +_efi_emulator_end:
> + .globl _efi_emulator_end
> diff --git a/drivers/firmware/efi/efi_emulator/emulator.h b/drivers/firmware/efi/efi_emulator/emulator.h
> new file mode 100644
> index 0000000000000..e524cbd92180c
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/emulator.h
> @@ -0,0 +1,66 @@
> +#include <linux/nls.h>
> +#include <linux/efi_emulator.h>
> +
> +/* Included from drivers/firmware/efi/libstub */
> +#include <efistub.h>
> +
> +#define EMULATOR_BASE_ADDR 0
> +
> +typedef union efi_rng_protocol efi_rng_protocol_t;
> +
> +union efi_rng_protocol {
> + struct {
> + efi_status_t (__efiapi *get_info)(efi_rng_protocol_t *,
> + unsigned long *,
> + efi_guid_t *);
> + efi_status_t (__efiapi *get_rng)(efi_rng_protocol_t *,
> + efi_guid_t *, unsigned long,
> + u8 *out);
> + };
> + struct {
> + u32 get_info;
> + u32 get_rng;
> + } mixed_mode;
> +};
> +
> +struct uki_info {
> + unsigned long initrd_base;
> + unsigned long initrd_sz;
> + unsigned long cmdline_base;
> + unsigned long cmdline_sz;
> +};
> +
> +extern struct efi_emulator_param *emulator_param;
> +extern efi_tcg2_protocol_t emulator_tcg2;
> +extern efi_cc_protocol_t emulator_cc;
> +extern efi_rng_protocol_t emulator_rng;
> +extern efi_system_table_t systabs;
> +extern efi_runtime_services_t rt_services;
> +extern char *heap_start, *heap_end, *heap_cur;
> +
> +void *aligned_alloc(size_t alignment, size_t size);
> +void *memcpy(void *dest, const void *src, size_t n);
> +void *memset(void *s, int c, size_t n);
> +int strcmp(const char *cs, const char *ct);
> +size_t wcslen(const wchar_t *str);
> +int wcscmp(const wchar_t *s1, const wchar_t *s2);
> +int printf(const char *format, ...);
> +void print_ucs2_string(efi_char16_t* ucs2_str);
> +
> +efi_status_t __emulator_allocate_pages(int alloc_type, int mem_type,
> + unsigned long nr_pages, efi_physical_addr_t *addr);
> +efi_status_t __emulator_allocate_pool(int mem_type, unsigned long sz,
> + void **pool);
> +efi_status_t emulator_get_memory_map(unsigned long *map_sz,
> + void *memmap, unsigned long *map_key, unsigned long *desc_sz,
> + unsigned int *desc_version);
> +
> +efi_status_t conjoin_table(efi_guid_t *uuid, void *table, efi_config_table_t *t);
> +
> +int initialize_emulator_service(struct efi_emulator_param *param);
> +void initialize_heap(struct efi_emulator_param *param);
> +void load_kernel_pe(char *kern_buf, unsigned long sz, efi_system_table_t *systabs);
> +void load_kernel_from_mem(efi_system_table_t *systabs);
> +void emulator_main(struct efi_emulator_param *param);
> +void emulator_entry(struct efi_emulator_param *param);
> +
> diff --git a/drivers/firmware/efi/efi_emulator/entry.c b/drivers/firmware/efi/efi_emulator/entry.c
> new file mode 100644
> index 0000000000000..c851a9acafa77
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/entry.c
> @@ -0,0 +1,57 @@
> +//SPDX-License-Identifier: GPL-2.0
> +#include <linux/types.h>
> +#include <linux/efi_emulator.h>
> +#include <asm/barrier.h>
> +#include <asm/sysreg.h>
> +#include <asm/elf.h>
> +#include <uapi/linux/elf.h>
> +
> +#include "emulator.h"
> +#include "earlycon.h"
> +
> +extern void enable_sctlr_el1(unsigned long scratch_reg);
> +static void arch_handle_mmu(struct efi_emulator_param *param)
> +{
> + if (!param->mmu_on && param->pgd_root) {
> + }
> +}
> +
> +extern const Elf64_Rela _rela_start[], _rela_end[];
> +
> +static void noinline arch_reloc_fixup(long delta)
> +{
> + unsigned long *apply_addr, res;
> + Elf64_Rela *rela;
> +
> + /* fix rela */
> + for (rela = (Elf64_Rela *)_rela_start; rela < _rela_end; rela++) {
> + //todo counterpart of R_AARCH64_RELATIVE on riscv
> + if (ELF64_R_TYPE(rela->r_info) != R_AARCH64_RELATIVE)
> + continue;
> + apply_addr = (unsigned long *)(rela->r_offset + delta);
> + res = rela->r_addend + delta;
> + *apply_addr = res;
> + }
> + // todo flush cache
> +
> +}
> +
> +/*
> + * Ensure this entry and @param is in the mapping before jump to it.
> + * It should be PIC and at the beginning of emulator.
> + * It should be memory aligned
> + */
> +void emulator_main(struct efi_emulator_param *param)
> +{
> + long delta = param->load_address - EMULATOR_BASE_ADDR;
> +
> + arch_handle_mmu(param);
> + arch_reloc_fixup(delta);
> + setup_earlycon(param);
> + printf("param:0x%lx, delta=0x%lx\n", (unsigned long)param, delta);
> + printf("kernel_img_start:0x%lx, sz:0x%lx\n", (unsigned long)param->kernel_img_start, (unsigned long)param->kernel_img_sz);
> + initialize_emulator_service(param);
> + initialize_heap(param);
> + load_kernel_pe((char *)param->kernel_img_start, param->kernel_img_sz,
> + &systabs);
> +}
> diff --git a/drivers/firmware/efi/efi_emulator/head.S b/drivers/firmware/efi/efi_emulator/head.S
> new file mode 100644
> index 0000000000000..8efa10f483a7c
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/head.S
> @@ -0,0 +1,10 @@
> +
> +.section ".head.text","ax"
> +
> +/* x0 holds the physical address of emulator_param */
> +emulator_entry:
> + ldr x1, [x0]
> + mov sp, x1
> + adrp x2, emulator_main
> + add x2, x2, #:lo12:emulator_main
> + br x2
> diff --git a/drivers/firmware/efi/efi_emulator/initrd.c b/drivers/firmware/efi/efi_emulator/initrd.c
> new file mode 100644
> index 0000000000000..771b6f8954e33
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/initrd.c
> @@ -0,0 +1,15 @@
> +// this file is a place holder to implment EFI_LOAD_FILE2_PROTOCOL.LoadFile(), not linked in yet
> +
> +static efi_status_t load_file(efi_load_file2_protocol_t *this,
> + efi_device_path_protocol_t *dp, bool boot_policy,
> + unsigned long buffer_sz, void *buffer)
> +{
> +
> + struct efi_vendor_dev_path p = dp;
> +
> + if (!efiguid_cmp(p->vendorguid , LINUX_EFI_INITRD_MEDIA_GUID))
> + if (uki_info.initrd_sz) {
> + memcpy(buffer, uki_info.initrd_base, uki_info.initrd_sz);
> + return;
> + }
> +}
> diff --git a/drivers/firmware/efi/efi_emulator/lib.c b/drivers/firmware/efi/efi_emulator/lib.c
> new file mode 100644
> index 0000000000000..f12aa40405650
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/lib.c
> @@ -0,0 +1,73 @@
> +//SPDX-License-Identifier: GPL-2.0
> +#include "emulator.h"
> +
> +void *memcpy(void *dest, const void *src, size_t count)
> +{
> + char *tmp = dest;
> + const char *s = src;
> +
> + while (count--)
> + *tmp++ = *s++;
> + return dest;
> +}
> +
> +void *memset(void *s, int c, size_t count)
> +{
> + char *xs = s;
> +
> + while (count--)
> + *xs++ = c;
> + return s;
> +}
> +
> +int memcmp(const void *cs, const void *ct, size_t count)
> +{
> + const unsigned char *su1, *su2;
> + int res = 0;
> +
> + for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
> + if ((res = *su1 - *su2) != 0)
> + break;
> + return res;
> +}
> +
> +size_t strlen(const char *s)
> +{
> + const char *sc;
> +
> + for (sc = s; *sc != '\0'; ++sc)
> + /* nothing */;
> + return sc - s;
> +}
> +
> +int strcmp(const char *cs, const char *ct)
> +{
> + unsigned char c1, c2;
> +
> + while (1) {
> + c1 = *cs++;
> + c2 = *ct++;
> + if (c1 != c2)
> + return c1 < c2 ? -1 : 1;
> + if (!c1)
> + break;
> + }
> + return 0;
> +}
> +
> +int wcscmp(const wchar_t *s1, const wchar_t *s2)
> +{
> + while (*s1 && (*s1 == *s2)) {
> + s1++;
> + s2++;
> + }
> + return (int)(*s1 - *s2);
> +}
> +
> +size_t wcslen(const wchar_t *str)
> +{
> + const wchar_t *s;
> +
> + for (s = str; *s; ++s);
> + return (s - str);
> +}
> diff --git a/drivers/firmware/efi/efi_emulator/memory.c b/drivers/firmware/efi/efi_emulator/memory.c
> new file mode 100644
> index 0000000000000..518ab7f30020e
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/memory.c
> @@ -0,0 +1,27 @@
> +//SPDX-License-Identifier: GPL-2.0
> +#include "emulator.h"
> +
> +char *heap_start, *heap_end, *heap_cur;
> +
> +void initialize_heap(struct efi_emulator_param *param)
> +{
> + heap_start = (char *)param->mempool_start;
> + heap_end = heap_start + param->mempool_sz;
> + heap_cur = heap_start;
> +}
> +
> +//2do, the memory management is more complicated since we need to distinguish EFI_BOOT_SERVICE, RUNTIME, LOADER memory descr
> +
> +void *aligned_alloc(size_t alignment, size_t size)
> +{
> + char *p;
> +
> + p = (char *)ALIGN((unsigned long)heap_cur, alignment);
> + heap_cur = p + size;
> +
> + //todo, update the efi_memory_desc to include this page, if it crosses the PAGE boundary
> + //as EFI_BOOT_SERVICE,
> + return p;
> +}
> +
> +
> diff --git a/drivers/firmware/efi/efi_emulator/memory_api.c b/drivers/firmware/efi/efi_emulator/memory_api.c
> new file mode 100644
> index 0000000000000..709bbddaa4679
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/memory_api.c
> @@ -0,0 +1,73 @@
> +#include <linux/efi.h>
> +#include <asm/efi.h>
> +
> +#include "emulator.h"
> +
> +/*
> + * mem_type affects the allocated chunk in efi_memory_desc_t's type. Later,
> + * kernel can know whether to reclaim them.
> + */
> +efi_status_t __emulator_allocate_pages(int alloc_type, int mem_type,
> + unsigned long nr_pages, efi_physical_addr_t *addr)
> +{
> + efi_physical_addr_t res;
> + efi_status_t status;
> +
> + if (alloc_type == EFI_ALLOCATE_ANY_PAGES) {
> + res = (efi_physical_addr_t)aligned_alloc(PAGE_SIZE, nr_pages << PAGE_SHIFT);
> + *addr = res;
> + status = EFI_SUCCESS;
> + } else if (alloc_type == EFI_ALLOCATE_MAX_ADDRESS) {
> + //tmp
> + res = (efi_physical_addr_t)aligned_alloc(PAGE_SIZE, nr_pages << PAGE_SHIFT);
> + *addr = res;
> + status = EFI_SUCCESS;
> + /* e.g. aarch64 kimage loaded alignment */
> + } else if (alloc_type == EFI_ALLOCATE_ADDRESS) {
> + //tmp, just aligned on 2MB as aarch64 boot protocol
> + res = (efi_physical_addr_t)aligned_alloc(1<<21, nr_pages << PAGE_SHIFT);
> + *addr = res;
> + status = EFI_SUCCESS;
> + }
> +
> + return status;
> +}
> +
> +//todo
> +efi_status_t __emulator_allocate_pool(int mem_type, unsigned long sz,
> + void **pool)
> +{
> + void *res;
> +
> + res = aligned_alloc(sizeof(unsigned long), sz);
> + *pool = res;
> + return EFI_SUCCESS;
> +}
> +
> +/* @memmap: only holds efi_memory_desc */
> +efi_status_t emulator_get_memory_map(unsigned long *map_sz,
> + void *memmap, unsigned long *map_key, unsigned long *desc_sz,
> + unsigned int *desc_version)
> +{
> + //todo rt_info.memmap will be accessed by kernel, so it should be marked as reserved
> + struct efi_boot_memmap *p = &emulator_param->rt_info.memmap;
> + //efi_memory_desc_t *desc = p->map;
> +
> + if (!map_sz || !desc_sz)
> + return EFI_INVALID_PARAMETER;
> + if (*map_sz < p->map_size || !memmap) {
> + *map_sz = p->map_size;
> + *desc_sz = p->desc_size;
> + return EFI_BUFFER_TOO_SMALL;
> + }
> +
> + /* desc range size*/
> + *map_sz = p->map_size;
> + memcpy(memmap, p->map, p->map_size);
> + if (!!desc_sz)
> + *desc_sz = p->desc_size;
> + if (!!desc_version)
> + *desc_version = p->desc_ver;
> +
> + return EFI_SUCCESS;
> +}
> diff --git a/drivers/firmware/efi/efi_emulator/misc.c b/drivers/firmware/efi/efi_emulator/misc.c
> new file mode 100644
> index 0000000000000..5a82adb6b193c
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/misc.c
> @@ -0,0 +1,76 @@
> +
> +#include <linux/efi.h>
> +#include <asm/efi.h>
> +
> +#include "emulator.h"
> +
> +static efi_status_t __efiapi emulator_tcg2_hash_log_extend_event(
> + efi_tcg2_protocol_t *this, u64 flags,
> + efi_physical_addr_t data_to_hash, u64 data_to_hash_len,
> + const efi_tcg2_event_t *efi_td_event)
> +{
> +
> + return EFI_SUCCESS;
> +
> +}
> +
> +efi_tcg2_protocol_t emulator_tcg2 = {
> + .hash_log_extend_event = emulator_tcg2_hash_log_extend_event,
> +};
> +
> +static efi_status_t __efiapi emulator_cc_map_pcr_to_mr_index(efi_cc_protocol_t *this,
> + u32 pcr_index, efi_cc_mr_index_t *mr_index)
> +{
> + return EFI_SUCCESS;
> +}
> +
> +static efi_status_t __efiapi emulator_cc_hash_log_extend_event(
> + efi_cc_protocol_t *this, u64 flags,
> + efi_physical_addr_t data_to_hash, u64 data_to_hash_len,
> + const efi_cc_event_t *efi_td_event)
> +{
> +
> + return EFI_SUCCESS;
> +}
> +
> +efi_cc_protocol_t emulator_cc = {
> + .hash_log_extend_event = emulator_cc_hash_log_extend_event,
> + .map_pcr_to_mr_index = emulator_cc_map_pcr_to_mr_index,
> +};
> +
> +static efi_status_t __efiapi emulator_get_rng(efi_rng_protocol_t * this,
> + efi_guid_t *uuid, unsigned long size,
> + u8 *out)
> +{
> + //in fact, disable aslr
> + *out = 0;
> + return EFI_SUCCESS;
> +}
> +
> +efi_rng_protocol_t emulator_rng = {
> + .get_rng = emulator_get_rng,
> +};
> +
> +static efi_status_t __efiapi emulator_get_memory_attributes(
> + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64 *)
> +{
> + return EFI_SUCCESS;
> +}
> +
> +static efi_status_t __efiapi emulator_set_memory_attributes(
> + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64)
> +{
> + return EFI_SUCCESS;
> +}
> +
> +static efi_status_t __efiapi emulator_clear_memory_attributes(
> + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64)
> +{
> + return EFI_SUCCESS;
> +}
> +
> +efi_memory_attribute_protocol_t emulator_memory_attribute = {
> + .get_memory_attributes = emulator_get_memory_attributes,
> + .set_memory_attributes = emulator_set_memory_attributes,
> + .clear_memory_attributes = emulator_clear_memory_attributes,
> +};
> diff --git a/drivers/firmware/efi/efi_emulator/pe_loader.c b/drivers/firmware/efi/efi_emulator/pe_loader.c
> new file mode 100644
> index 0000000000000..ff921d611a287
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/pe_loader.c
> @@ -0,0 +1,124 @@
> +
> +#include <linux/pe.h>
> +#include <linux/efi.h>
> +#include <asm/efi.h>
> +#include "emulator.h"
> +
> +#define VALID_PAYLOAD (IMAGE_SCN_CNT_CODE |IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_CNT_UNINITIALIZED_DATA)
> +
> +typedef efi_status_t (*uefi_pe_entry)(efi_handle_t handle, efi_system_table_t *systab);
> +
> +struct pe_instance {
> + uefi_pe_entry entry;
> + efi_handle_t handle;
> +};
> +
> +#if 0
> +static struct uki_info uki_info;
> +#endif
> +
> +static int load_pe(char *buf, unsigned long buf_sz, unsigned long pe_hdr_offset,
> + struct pe_instance *inst, efi_system_table_t *systab)
> +{
> + unsigned long exec_sz, load_addr, delta;
> + struct pe_hdr *pe_hdr;
> + struct pe32plus_opt_hdr *opt_hdr;
> + struct section_header *sect_hdr;
> + int section_nr, i;
> + char *src, *dst;
> +
> + pe_hdr = (struct pe_hdr *)buf;
> + section_nr = pe_hdr->sections;
> + opt_hdr = (struct pe32plus_opt_hdr *)(buf + sizeof(struct pe_hdr));
> + sect_hdr = (struct section_header *)((char *)opt_hdr + pe_hdr->opt_hdr_size);
> + exec_sz = opt_hdr->image_size - opt_hdr->header_size;
> +
> + /*
> + * The allocated memory should meet the section aligment requirement.
> + * The first section is loaded at load_addr.
> + */
> + load_addr = (unsigned long)aligned_alloc(opt_hdr->section_align, exec_sz);
> +
> + delta = load_addr - sect_hdr->virtual_address;
> + /*copy section to segment */
> + //opt_hdr->section_align;
> + for (i = 0; i < section_nr; i++) {
> + if (!(sect_hdr->flags & VALID_PAYLOAD)) {
> + sect_hdr++;
> + continue;
> + }
> + /* data_addr is relative to the whole file */
> + src = buf + sect_hdr->data_addr - pe_hdr_offset;
> + dst = (char *)(sect_hdr->virtual_address + delta);
> + memcpy(dst, src, sect_hdr->raw_data_size);
> + memset(dst + sect_hdr->raw_data_size, 0, sect_hdr->virtual_size - sect_hdr->raw_data_size);
> +#if 0
> + //2do, for UKI
> + if (strcmp(sect_hdr->name, ".initrd")) {
> + uki_info.initrd_base = dst;
> + uki_info.initrd_sz = sect_hdr->raw_data_size;
> + }
> + if (strcmp(sect_hdr->name, ".cmdline")) {
> + uki_info.cmdline_base = dst;
> + uki_info.cmdline_sz = sect_hdr->raw_data_size;
> + }
> +#endif
For UKI, I have a wrong understanding, all code related with uki_info
struct should be eliminated.
And see the next comment for detail.
> + sect_hdr++;
> + }
> +
> + /* no need to resolve relocation */
> +
> + /* As for SP, using the current value */
> +
> + inst->entry = (uefi_pe_entry)(opt_hdr->entry_point + delta);
> + inst->handle = (efi_handle_t)load_addr;
> +
> + printf("entry_point:0x%lx, delta:0x%lx, final inst's entry at:0x%lx\n",
> + opt_hdr->entry_point, delta, inst->entry);
> + return 0;
> +}
> +
> +static int launch_pe(char *pe_buf, u64 buf_sz, unsigned long pe_hdr_offset, efi_system_table_t *systab)
> +{
> + struct pe_instance inst;
> +
> + load_pe(pe_buf, buf_sz, pe_hdr_offset, &inst, systab);
> + inst.entry(inst.handle, systab);
> + return 0;
> +}
> +
> +struct input_param_passed_by {
> + u64 kernel_pa_start;
> + u64 kernel_sz;
> + u64 pgtable;
> + u64 memory_descs;
> +};
> +
> +
> +/* see drivers/firmware/efi/libstub/zboot-header.S */
> +struct linux_pe_zboot_header {
> + uint32_t mz_magic;
> + uint32_t image_type;
> + uint32_t payload_offset;
> + uint32_t payload_size;
> + uint32_t reserved[2];
> + uint32_t compress_type;
> +};
> +
> +void load_kernel_pe(char *kern_buf, unsigned long sz, efi_system_table_t *systabs)
> +{
> + struct linux_pe_zboot_header *hdr;
> + u32 pe_hdr_offset;
> + char *buf = kern_buf;
> +
> + hdr = (struct linux_pe_zboot_header *)buf;
> + /* Check mz signature */
> + if (memcmp(&hdr->image_type, "zimg", sizeof(hdr->image_type)))
> + return;
> + pe_hdr_offset = *((u32 *)(buf + 0x3c));
> + /* */
> + buf += pe_hdr_offset;
> + sz -= pe_hdr_offset;
> + launch_pe(buf, sz, pe_hdr_offset, systabs);
> +}
> +
> diff --git a/drivers/firmware/efi/efi_emulator/printf.c b/drivers/firmware/efi/efi_emulator/printf.c
> new file mode 100644
> index 0000000000000..367d1ede98422
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/printf.c
> @@ -0,0 +1,389 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* The most of this file is copied from arch/x86/boot/printf.c */
> +
> +#include <linux/ctype.h>
> +#include <linux/efi.h>
> +
> +#include "earlycon.h"
> +#include "emulator.h"
> +
> +static int skip_atoi(const char **s)
> +{
> + int i = 0;
> +
> + while (isdigit(**s))
> + i = i * 10 + *((*s)++) - '0';
> + return i;
> +}
> +
> +#define ZEROPAD 1 /* pad with zero */
> +#define SIGN 2 /* unsigned/signed long */
> +#define PLUS 4 /* show plus */
> +#define SPACE 8 /* space if plus */
> +#define LEFT 16 /* left justified */
> +#define SMALL 32 /* Must be 32 == 0x20 */
> +#define SPECIAL 64 /* 0x */
> +
> +#define __do_div(n, base) ({ \
> +int __res; \
> +__res = ((unsigned long) n) % (unsigned) base; \
> +n = ((unsigned long) n) / (unsigned) base; \
> +__res; })
> +
> +static char *number(char *str, long num, int base, int size, int precision,
> + int type)
> +{
> + /* we are called with base 8, 10 or 16, only, thus don't need "G..." */
> + static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
> +
> + char tmp[66];
> + char c, sign, locase;
> + int i;
> +
> + /* locase = 0 or 0x20. ORing digits or letters with 'locase'
> + * produces same digits or (maybe lowercased) letters */
> + locase = (type & SMALL);
> + if (type & LEFT)
> + type &= ~ZEROPAD;
> + if (base < 2 || base > 16)
> + return NULL;
> + c = (type & ZEROPAD) ? '0' : ' ';
> + sign = 0;
> + if (type & SIGN) {
> + if (num < 0) {
> + sign = '-';
> + num = -num;
> + size--;
> + } else if (type & PLUS) {
> + sign = '+';
> + size--;
> + } else if (type & SPACE) {
> + sign = ' ';
> + size--;
> + }
> + }
> + if (type & SPECIAL) {
> + if (base == 16)
> + size -= 2;
> + else if (base == 8)
> + size--;
> + }
> + i = 0;
> + if (num == 0)
> + tmp[i++] = '0';
> + else
> + while (num != 0)
> + tmp[i++] = (digits[__do_div(num, base)] | locase);
> + if (i > precision)
> + precision = i;
> + size -= precision;
> + if (!(type & (ZEROPAD + LEFT)))
> + while (size-- > 0)
> + *str++ = ' ';
> + if (sign)
> + *str++ = sign;
> + if (type & SPECIAL) {
> + if (base == 8)
> + *str++ = '0';
> + else if (base == 16) {
> + *str++ = '0';
> + *str++ = ('X' | locase);
> + }
> + }
> + if (!(type & LEFT))
> + while (size-- > 0)
> + *str++ = c;
> + while (i < precision--)
> + *str++ = '0';
> + while (i-- > 0)
> + *str++ = tmp[i];
> + while (size-- > 0)
> + *str++ = ' ';
> + return str;
> +}
> +
> +size_t strnlen(const char *s, size_t count)
> +{
> + const char *sc;
> +
> + for (sc = s; count-- && *sc != '\0'; ++sc)
> + /* nothing */;
> + return sc - s;
> +}
> +
> +int vsprintf(char *buf, const char *fmt, va_list args)
> +{
> + int len;
> + unsigned long num;
> + int i, base;
> + char *str;
> + const char *s;
> +
> + int flags; /* flags to number() */
> +
> + int field_width; /* width of output field */
> + int precision; /* min. # of digits for integers; max
> + number of chars for from string */
> + int qualifier; /* 'h', 'l', or 'L' for integer fields */
> +
> + for (str = buf; *fmt; ++fmt) {
> + if (*fmt != '%') {
> + *str++ = *fmt;
> + continue;
> + }
> +
> + /* process flags */
> + flags = 0;
> + repeat:
> + ++fmt; /* this also skips first '%' */
> + switch (*fmt) {
> + case '-':
> + flags |= LEFT;
> + goto repeat;
> + case '+':
> + flags |= PLUS;
> + goto repeat;
> + case ' ':
> + flags |= SPACE;
> + goto repeat;
> + case '#':
> + flags |= SPECIAL;
> + goto repeat;
> + case '0':
> + flags |= ZEROPAD;
> + goto repeat;
> + }
> +
> + /* get field width */
> + field_width = -1;
> + if (isdigit(*fmt))
> + field_width = skip_atoi(&fmt);
> + else if (*fmt == '*') {
> + ++fmt;
> + /* it's the next argument */
> + field_width = va_arg(args, int);
> + if (field_width < 0) {
> + field_width = -field_width;
> + flags |= LEFT;
> + }
> + }
> +
> + /* get the precision */
> + precision = -1;
> + if (*fmt == '.') {
> + ++fmt;
> + if (isdigit(*fmt))
> + precision = skip_atoi(&fmt);
> + else if (*fmt == '*') {
> + ++fmt;
> + /* it's the next argument */
> + precision = va_arg(args, int);
> + }
> + if (precision < 0)
> + precision = 0;
> + }
> +
> + /* get the conversion qualifier */
> + qualifier = -1;
> + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
> + qualifier = *fmt;
> + ++fmt;
> + }
> +
> + /* default base */
> + base = 10;
> +
> + switch (*fmt) {
> + case 'c':
> + if (!(flags & LEFT))
> + while (--field_width > 0)
> + *str++ = ' ';
> + *str++ = (unsigned char)va_arg(args, int);
> + while (--field_width > 0)
> + *str++ = ' ';
> + continue;
> +
> + case 's':
> + s = va_arg(args, char *);
> + len = strnlen(s, precision);
> +
> + if (!(flags & LEFT))
> + while (len < field_width--)
> + *str++ = ' ';
> + for (i = 0; i < len; ++i)
> + *str++ = *s++;
> + while (len < field_width--)
> + *str++ = ' ';
> + continue;
> +
> + case 'p':
> + if (field_width == -1) {
> + field_width = 2 * sizeof(void *);
> + flags |= ZEROPAD;
> + }
> + str = number(str,
> + (unsigned long)va_arg(args, void *), 16,
> + field_width, precision, flags);
> + continue;
> +
> + case 'n':
> + if (qualifier == 'l') {
> + long *ip = va_arg(args, long *);
> + *ip = (str - buf);
> + } else {
> + int *ip = va_arg(args, int *);
> + *ip = (str - buf);
> + }
> + continue;
> +
> + case '%':
> + *str++ = '%';
> + continue;
> +
> + /* integer number formats - set up the flags and "break" */
> + case 'o':
> + base = 8;
> + break;
> +
> + case 'x':
> + flags |= SMALL;
> + fallthrough;
> + case 'X':
> + base = 16;
> + break;
> +
> + case 'd':
> + case 'i':
> + flags |= SIGN;
> + case 'u':
> + break;
> +
> + default:
> + *str++ = '%';
> + if (*fmt)
> + *str++ = *fmt;
> + else
> + --fmt;
> + continue;
> + }
> + if (qualifier == 'l')
> + num = va_arg(args, unsigned long);
> + else if (qualifier == 'h') {
> + num = (unsigned short)va_arg(args, int);
> + if (flags & SIGN)
> + num = (short)num;
> + } else if (flags & SIGN)
> + num = va_arg(args, int);
> + else
> + num = va_arg(args, unsigned int);
> + str = number(str, num, base, field_width, precision, flags);
> + }
> + *str = '\0';
> + return str - buf;
> +}
> +
> +int sprintf(char *buf, const char *fmt, ...)
> +{
> + va_list args;
> + int i;
> +
> + va_start(args, fmt);
> + i = vsprintf(buf, fmt, args);
> + va_end(args);
> + return i;
> +}
> +
> +static struct earlycon *con;
> +
> +static int puts(const char *s)
> +{
> + if (con)
> + return con->put_str(s, con->data);
> + else
> + return 0;
> +}
> +
> +int printf(const char *fmt, ...)
> +{
> + char printf_buf[1024];
> + va_list args;
> + int printed;
> +
> + va_start(args, fmt);
> + printed = vsprintf(printf_buf, fmt, args);
> + va_end(args);
> +
> + puts(printf_buf);
> +
> + return printed;
> +}
> +
> +
> +static size_t utf16_to_ascii(char *s, efi_char16_t c16)
> +{
> + unsigned char lead;
> + size_t len;
> +
> + if (c16 < 0x80) {
> + /* 1-byte sequence */
> + if (s) *s = c16;
> + len = 1;
> + } else if (c16 < 0x800) {
> + /* 2-byte sequence */
> + lead = 0xC0;
> + len = 2;
> + } else {
> + /* 3-byte sequence */
> + lead = 0xE0;
> + len = 3;
> + }
> +
> + if (s) {
> + switch (len) {
> + case 3:
> + s[2] = 0x80 | (c16 & 0x3F);
> + c16 >>= 6;
> + fallthrough;
> + case 2:
> + s[1] = 0x80 | (c16 & 0x3F);
> + c16 >>= 6;
> + fallthrough;
> + case 1:
> + s[0] = lead | c16;
> + }
> + }
> + return len;
> +}
> +
> +/* Convert the UCS-2 string to a UTF-8 string */
> +void print_ucs2_string(efi_char16_t* ucs2_str)
> +{
> + char utf8_str[1024];
> + char* p = utf8_str;
> +
> + while (*ucs2_str)
> + p += utf16_to_ascii(p, *ucs2_str++);
> +
> + /* Null-terminate the UTF-8 string */
> + *p = '\0';
> + /* Print the UTF-8 string */
> + printf("%s\n", utf8_str);
> +}
> +
> +static struct earlycon *all_con_types[] = { &pl011, };
> +
> +void setup_earlycon(struct efi_emulator_param *param)
> +{
> + struct earlycon *p;
> + int i;
> +
> + for (i = 0; i < sizeof(all_con_types) / sizeof(struct earlycon *); i++) {
> +
> + p = all_con_types[i];
> + if (p->match(param, p->name)) {
> + con = p;
> + p->reset(p->data);
> + break;
> + }
> + }
> +}
> diff --git a/drivers/firmware/efi/efi_emulator/runtime_service.c b/drivers/firmware/efi/efi_emulator/runtime_service.c
> new file mode 100644
> index 0000000000000..87e49a8d4e2db
> --- /dev/null
> +++ b/drivers/firmware/efi/efi_emulator/runtime_service.c
> @@ -0,0 +1,28 @@
> +//SPDX-License-Identifier: GPL-2.0
> +
> +#include "emulator.h"
> +
> +static efi_status_t emulator_get_variable(efi_char16_t *name, efi_guid_t *vendor,
> + u32 *attr, unsigned long *data_size, void *data)
> +{
> + if (!efi_guidcmp(*vendor, EFI_GLOBAL_VARIABLE_GUID)) {
> + if (wcscmp(name, L"SecureBoot"))
> + return EFI_NOT_FOUND;
> + }
> + return EFI_NOT_FOUND;
> +}
> +
> +static efi_status_t emulator_set_virtual_address_map(unsigned long memory_map_size,
> + unsigned long descriptor_size,
> + u32 descriptor_version,
> + efi_memory_desc_t *virtual_map)
> +{
> + /* The first kernel has called this one-shot service */
> + return EFI_NOT_FOUND;
> +}
> +
> +efi_runtime_services_t rt_services = {
> + .set_virtual_address_map = emulator_set_virtual_address_map,
> + .get_variable = emulator_get_variable,
> +
> +};
> diff --git a/include/linux/efi_emulator.h b/include/linux/efi_emulator.h
> new file mode 100644
> index 0000000000000..cb977539cf05f
> --- /dev/null
> +++ b/include/linux/efi_emulator.h
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _LINUX_EFI_EMULATOR_H
> +#define _LINUX_EFI_EMULATOR_H
> +
> +#include <linux/types.h>
> +#include <linux/nls.h>
> +#include <linux/efi.h>
> +
> +//todo, arch abstraction, for x86, it is efi_info
> +struct efi_rt_info {
> + const efi_runtime_services_t *runtime; /* EFI runtime services table */
> + unsigned int runtime_version; /* Runtime services version */
> + u32 runtime_supported_mask;
> + /* Build systab tables from the following */
> + unsigned int systab_nr_tables;
> + efi_config_table_t systab_tables[20];
> + struct efi_boot_memmap memmap;
> +};
> +
> +/* 1st kernel passes information through this struct */
> +struct efi_emulator_param {
> + unsigned long sp;
> + /* Should be page-aligned */
> + unsigned long load_address;
> + unsigned int sz_in_byte;
> + wchar_t cmdline[512];
> + bool noefi_boot;
> + bool print_enabled;
> + char earlycon_name[16];
> + phys_addr_t earlycon_reg_base;
> + unsigned long earlycon_reg_sz;
> +
> + bool mmu_on;
> + /* root of pgtable */
> + phys_addr_t pgd_root;
> + phys_addr_t kernel_img_start;
> + unsigned long kernel_img_sz;
> + phys_addr_t dtb;
> + phys_addr_t mempool_start;
> + unsigned long mempool_sz;
> + struct efi_rt_info rt_info;
> +};
> +
> +extern unsigned char _efi_emulator_start[], _efi_emulator_end[];
> +#endif
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index f0e9f8eda7a3c..cff6b6869498b 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -325,6 +325,7 @@ struct kimage {
> unsigned int hotplug_support:1;
> #endif
>
> + bool is_pe;
> #ifdef ARCH_HAS_KIMAGE_ARCH
> struct kimage_arch arch;
> #endif
> @@ -462,6 +463,7 @@ static inline int arch_kexec_post_alloc_pages(void *vaddr, unsigned int pages, g
> static inline void arch_kexec_pre_free_pages(void *vaddr, unsigned int pages) { }
> #endif
>
> +extern phys_addr_t arch_emulator_prepare_pgtable(struct kimage *kimage);
> extern bool kexec_file_dbg_print;
>
> #define kexec_dprintk(fmt, arg...) \
> --
> 2.41.0
>
More information about the kexec
mailing list