Merge branch 'arm/queue' into 'master'
arm/arm64: EFI improvements and no more MAX_SMP probing
See merge request kvm-unit-tests/kvm-unit-tests!53
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 71d986e..ff34b1f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -44,7 +44,35 @@
selftest-vectors-user
timer
| tee results.txt
- - if grep -q FAIL results.txt ; then exit 1 ; fi
+ - grep -q PASS results.txt && ! grep -q FAIL results.txt
+
+build-aarch64-efi:
+ extends: .intree_template
+ script:
+ - dnf install -y qemu-system-aarch64 gcc-aarch64-linux-gnu edk2-aarch64
+ - ./configure --arch=aarch64 --cross-prefix=aarch64-linux-gnu- --enable-efi --enable-efi-direct
+ - make -j2
+ - ACCEL=tcg MAX_SMP=8 ./run_tests.sh
+ selftest-setup
+ selftest-smp
+ selftest-vectors-kernel
+ selftest-vectors-user
+ | tee results.txt
+ - grep -q PASS results.txt && ! grep -q FAIL results.txt
+
+build-aarch64-efi-acpi:
+ extends: .intree_template
+ script:
+ - dnf install -y qemu-system-aarch64 gcc-aarch64-linux-gnu edk2-aarch64
+ - ./configure --arch=aarch64 --cross-prefix=aarch64-linux-gnu- --enable-efi --enable-efi-direct
+ - make -j2
+ - EFI_USE_ACPI=y ACCEL=tcg MAX_SMP=8 ./run_tests.sh
+ selftest-setup
+ selftest-smp
+ selftest-vectors-kernel
+ selftest-vectors-user
+ | tee results.txt
+ - grep -q PASS results.txt && ! grep -q FAIL results.txt
build-arm:
extends: .outoftree_template
@@ -59,7 +87,7 @@
pci-test pmu-cycle-counter gicv2-ipi gicv2-mmio gicv3-ipi gicv2-active
gicv3-active
| tee results.txt
- - if grep -q FAIL results.txt ; then exit 1 ; fi
+ - grep -q PASS results.txt && ! grep -q FAIL results.txt
build-ppc64be:
extends: .outoftree_template
diff --git a/arm/efi/crt0-efi-aarch64.S b/arm/efi/crt0-efi-aarch64.S
index 5d0dc04..71ce279 100644
--- a/arm/efi/crt0-efi-aarch64.S
+++ b/arm/efi/crt0-efi-aarch64.S
@@ -111,17 +111,10 @@
.align 12
_start:
- stp x29, x30, [sp, #-16]!
-
- /* Align sp; this is necessary due to way we store cpu0's thread_info */
+ stp x29, x30, [sp, #-32]!
mov x29, sp
- mov x30, sp
- and x30, x30, #THREAD_MASK
- mov sp, x30
- str x29, [sp, #-16]!
- stp x0, x1, [sp, #-16]!
-
+ stp x0, x1, [sp, #16]
mov x2, x0
mov x3, x1
adr x0, ImageBase
@@ -130,12 +123,32 @@
bl _relocate
cbnz x0, 0f
- ldp x0, x1, [sp], #16
+ ldp x0, x1, [sp, #16]
+
+ /*
+ * Switch to our own stack and align sp; this is necessary due
+ * to way we store cpu0's thread_info
+ */
+ adrp x2, stacktop
+ add x2, x2, :lo12:stacktop
+ and x2, x2, #THREAD_MASK
+ mov x3, sp
+ mov sp, x2
+ stp xzr, xzr, [sp, #-16]!
+ mov x29, sp
+ str x3, [sp, #-16]!
+
bl efi_main
/* Restore sp */
ldr x30, [sp], #16
- mov sp, x30
+ mov sp, x30
-0: ldp x29, x30, [sp], #16
+0: ldp x29, x30, [sp], #32
ret
+
+ .section .data
+
+.balign 65536
+.space 65536
+stacktop:
diff --git a/arm/efi/elf_aarch64_efi.lds b/arm/efi/elf_aarch64_efi.lds
index 836d982..7a4192b 100644
--- a/arm/efi/elf_aarch64_efi.lds
+++ b/arm/efi/elf_aarch64_efi.lds
@@ -13,6 +13,7 @@
*(.rodata*)
. = ALIGN(16);
}
+ . = ALIGN(4096);
_etext = .;
_text_size = . - _text;
.dynamic : { *(.dynamic) }
diff --git a/arm/efi/run b/arm/efi/run
index 6872c33..f07a6e5 100755
--- a/arm/efi/run
+++ b/arm/efi/run
@@ -1,7 +1,5 @@
#!/bin/bash
-set -e
-
if [ $# -eq 0 ]; then
echo "Usage $0 TEST_CASE [QEMU_ARGS]"
exit 2
@@ -13,7 +11,6 @@
fi
source config.mak
source scripts/arch-run.bash
-source scripts/common.bash
if [ -f /usr/share/qemu-efi-aarch64/QEMU_EFI.fd ]; then
DEFAULT_UEFI=/usr/share/qemu-efi-aarch64/QEMU_EFI.fd
@@ -21,14 +18,17 @@
DEFAULT_UEFI=/usr/share/edk2/aarch64/QEMU_EFI.silent.fd
fi
+KERNEL_NAME=$1
+
: "${EFI_SRC:=$TEST_DIR}"
: "${EFI_UEFI:=$DEFAULT_UEFI}"
: "${EFI_TEST:=efi-tests}"
-: "${EFI_CASE:=$(basename $1 .efi)}"
+: "${EFI_CASE:=$(basename $KERNEL_NAME .efi)}"
+: "${EFI_TESTNAME:=$TESTNAME}"
+: "${EFI_TESTNAME:=$EFI_CASE}"
+: "${EFI_CASE_DIR:="$EFI_TEST/$EFI_TESTNAME"}"
: "${EFI_VAR_GUID:=97ef3e03-7329-4a6a-b9ba-6c1fdcc5f823}"
-[ "$EFI_USE_ACPI" = "y" ] || EFI_USE_DTB=y
-
if [ ! -f "$EFI_UEFI" ]; then
echo "UEFI firmware not found."
echo "Please specify the path with the env variable EFI_UEFI"
@@ -51,25 +51,45 @@
shift 1
fi
done
+if [ "$EFI_USE_ACPI" != "y" ]; then
+ qemu_args+=(-machine acpi=off)
+fi
if [ "$EFI_CASE" = "_NO_FILE_4Uhere_" ]; then
- EFI_CASE=dummy
+ EFI_CASE_DIR="$EFI_TEST/dummy"
+ mkdir -p "$EFI_CASE_DIR"
+ $TEST_DIR/run \
+ $EFI_CASE \
+ -bios "$EFI_UEFI" \
+ -drive file.dir="$EFI_CASE_DIR/",file.driver=vvfat,file.rw=on,format=raw,if=virtio \
+ "${qemu_args[@]}"
+ exit
fi
-: "${EFI_CASE_DIR:="$EFI_TEST/$EFI_CASE"}"
-mkdir -p "$EFI_CASE_DIR"
+uefi_shell_run()
+{
+ mkdir -p "$EFI_CASE_DIR"
+ cp "$EFI_SRC/$EFI_CASE.efi" "$EFI_CASE_DIR/"
+ echo "@echo -off" > "$EFI_CASE_DIR/startup.nsh"
+ if [ "$EFI_USE_ACPI" != "y" ]; then
+ FDT_BASENAME="dtb"
+ UEFI_SHELL_RUN=y $TEST_DIR/run -machine dumpdtb="$EFI_CASE_DIR/$FDT_BASENAME" "${qemu_args[@]}"
+ echo "setvar fdtfile -guid $EFI_VAR_GUID -rt =L\"$FDT_BASENAME\"" >> "$EFI_CASE_DIR/startup.nsh"
+ fi
+ echo "$EFI_CASE.efi" "${cmd_args[@]}" >> "$EFI_CASE_DIR/startup.nsh"
-cp "$EFI_SRC/$EFI_CASE.efi" "$EFI_TEST/$EFI_CASE/"
-echo "@echo -off" > "$EFI_TEST/$EFI_CASE/startup.nsh"
-if [ "$EFI_USE_DTB" = "y" ]; then
- qemu_args+=(-machine acpi=off)
- FDT_BASENAME="dtb"
- $(EFI_RUN=y $TEST_DIR/run -machine dumpdtb="$EFI_TEST/$EFI_CASE/$FDT_BASENAME" "${qemu_args[@]}")
- echo "setvar fdtfile -guid $EFI_VAR_GUID -rt =L\"$FDT_BASENAME\"" >> "$EFI_TEST/$EFI_CASE/startup.nsh"
+ UEFI_SHELL_RUN=y $TEST_DIR/run \
+ -bios "$EFI_UEFI" \
+ -drive file.dir="$EFI_CASE_DIR/",file.driver=vvfat,file.rw=on,format=raw,if=virtio \
+ "${qemu_args[@]}"
+}
+
+if [ "$EFI_DIRECT" = "y" ]; then
+ $TEST_DIR/run \
+ $KERNEL_NAME \
+ -append "$(basename $KERNEL_NAME) ${cmd_args[@]}" \
+ -bios "$EFI_UEFI" \
+ "${qemu_args[@]}"
+else
+ uefi_shell_run
fi
-echo "$EFI_CASE.efi" "${cmd_args[@]}" >> "$EFI_TEST/$EFI_CASE/startup.nsh"
-
-EFI_RUN=y $TEST_DIR/run \
- -bios "$EFI_UEFI" \
- -drive file.dir="$EFI_TEST/$EFI_CASE/",file.driver=vvfat,file.rw=on,format=raw,if=virtio \
- "${qemu_args[@]}"
diff --git a/arm/run b/arm/run
index ac64b3b..efdd44c 100755
--- a/arm/run
+++ b/arm/run
@@ -60,7 +60,7 @@
exit 2
fi
-if [ "$EFI_RUN" != "y" ]; then
+if [ "$UEFI_SHELL_RUN" != "y" ] && [ "$EFI_USE_ACPI" != "y" ]; then
chr_testdev='-device virtio-serial-device'
chr_testdev+=' -device virtconsole,chardev=ctd -chardev testdev,id=ctd'
fi
@@ -75,8 +75,10 @@
command+=" -display none -serial stdio"
command="$(migration_cmd) $(timeout_cmd) $command"
-if [ "$EFI_RUN" = "y" ]; then
+if [ "$UEFI_SHELL_RUN" = "y" ]; then
ENVIRON_DEFAULT=n run_qemu_status $command "$@"
+elif [ "$EFI_USE_ACPI" = "y" ]; then
+ run_qemu_status $command -kernel "$@"
else
run_qemu $command -kernel "$@"
fi
diff --git a/configure b/configure
index 4a00bdf..51edee8 100755
--- a/configure
+++ b/configure
@@ -32,6 +32,7 @@
page_size=
earlycon=
efi=
+efi_direct=
# Enable -Werror by default for git repositories only (i.e. developer builds)
if [ -e "$srcdir"/.git ]; then
@@ -90,6 +91,11 @@
--[enable|disable]-efi Boot and run from UEFI (disabled by default, x86_64 and arm64 only)
--[enable|disable]-werror
Select whether to compile with the -Werror compiler flag
+ --[enable|disable]-efi-direct
+ Select whether to run EFI tests directly with QEMU's -kernel
+ option. When not enabled, tests will be placed in an EFI file
+ system and run from the UEFI shell. Ignored when efi isn't enabled.
+ (arm64 only)
EOF
exit 1
}
@@ -169,6 +175,12 @@
--disable-efi)
efi=n
;;
+ --enable-efi-direct)
+ efi_direct=y
+ ;;
+ --disable-efi-direct)
+ efi_direct=n
+ ;;
--enable-werror)
werror=-Werror
;;
@@ -186,6 +198,10 @@
esac
done
+if [ -z "$efi" ] || [ "$efi" = "n" ]; then
+ [ "$efi_direct" = "y" ] && efi_direct=
+fi
+
if [ -n "$host_key_document" ] && [ ! -f "$host_key_document" ]; then
echo "Host key document doesn't exist at the specified location."
exit 1
@@ -428,6 +444,7 @@
HOST_KEY_DOCUMENT=$host_key_document
CONFIG_DUMP=$enable_dump
CONFIG_EFI=$efi
+EFI_DIRECT=$efi_direct
CONFIG_WERROR=$werror
GEN_SE_HEADER=$gen_se_header
EOF
diff --git a/lib/arm/mmu.c b/lib/arm/mmu.c
index eb5e82a..9dce7da 100644
--- a/lib/arm/mmu.c
+++ b/lib/arm/mmu.c
@@ -221,12 +221,8 @@
mmu_idmap = alloc_page();
for (r = mem_regions; r->end; ++r) {
- if (r->flags & MR_F_IO) {
+ if (r->flags & (MR_F_IO | MR_F_RESERVED)) {
continue;
- } else if (r->flags & MR_F_RESERVED) {
- /* Reserved pages need to be writable for whatever reserved them */
- mmu_set_range_ptes(mmu_idmap, r->start, r->start, r->end,
- __pgprot(PTE_WBWA));
} else if (r->flags & MR_F_CODE) {
/* armv8 requires code shared between EL1 and EL0 to be read-only */
mmu_set_range_ptes(mmu_idmap, r->start, r->start, r->end,
diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index 0382cbd..2f649af 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -136,9 +136,28 @@
#endif
}
-static void mem_init(phys_addr_t freemem_start)
+static void mem_allocator_init(phys_addr_t freemem_start, phys_addr_t freemem_end)
{
phys_addr_t base, top;
+
+ freemem_start = PAGE_ALIGN(freemem_start);
+ freemem_end &= PAGE_MASK;
+
+ phys_alloc_init(freemem_start, freemem_end - freemem_start);
+ phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
+
+ phys_alloc_get_unused(&base, &top);
+ base = PAGE_ALIGN(base);
+ top &= PAGE_MASK;
+ assert(sizeof(long) == 8 || !(base >> 32));
+ if (sizeof(long) != 8 && (top >> 32) != 0)
+ top = ((uint64_t)1 << 32);
+ page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT);
+ page_alloc_ops_enable();
+}
+
+static void mem_init(phys_addr_t freemem_start)
+{
struct mem_region *freemem, *r, mem = {
.start = (phys_addr_t)-1,
};
@@ -169,45 +188,60 @@
__phys_offset = mem.start; /* PHYS_OFFSET */
__phys_end = mem.end; /* PHYS_END */
- phys_alloc_init(freemem_start, freemem->end - freemem_start);
- phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
+ mem_allocator_init(freemem_start, freemem->end);
+}
- phys_alloc_get_unused(&base, &top);
- base = PAGE_ALIGN(base);
- top = top & PAGE_MASK;
- assert(sizeof(long) == 8 || !(base >> 32));
- if (sizeof(long) != 8 && (top >> 32) != 0)
- top = ((uint64_t)1 << 32);
- page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT);
- page_alloc_ops_enable();
+static void freemem_push_fdt(void **freemem, const void *fdt)
+{
+ u32 fdt_size;
+ int ret;
+
+ fdt_size = fdt_totalsize(fdt);
+ ret = fdt_move(fdt, *freemem, fdt_size);
+ assert(ret == 0);
+ ret = dt_init(*freemem);
+ assert(ret == 0);
+ *freemem += fdt_size;
+}
+
+static void freemem_push_dt_initrd(void **freemem)
+{
+ const char *tmp;
+ int ret;
+
+ ret = dt_get_initrd(&tmp, &initrd_size);
+ assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
+ if (ret == 0) {
+ initrd = *freemem;
+ memmove(initrd, tmp, initrd_size);
+ *freemem += initrd_size;
+ }
+}
+
+static void initrd_setup(void)
+{
+ char *env;
+
+ if (!initrd)
+ return;
+
+ /* environ is currently the only file in the initrd */
+ env = malloc(initrd_size);
+ memcpy(env, initrd, initrd_size);
+ setup_env(env, initrd_size);
}
void setup(const void *fdt, phys_addr_t freemem_start)
{
void *freemem;
- const char *bootargs, *tmp;
- u32 fdt_size;
+ const char *bootargs;
int ret;
assert(sizeof(long) == 8 || freemem_start < (3ul << 30));
freemem = (void *)(unsigned long)freemem_start;
- /* Move the FDT to the base of free memory */
- fdt_size = fdt_totalsize(fdt);
- ret = fdt_move(fdt, freemem, fdt_size);
- assert(ret == 0);
- ret = dt_init(freemem);
- assert(ret == 0);
- freemem += fdt_size;
-
- /* Move the initrd to the top of the FDT */
- ret = dt_get_initrd(&tmp, &initrd_size);
- assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
- if (ret == 0) {
- initrd = freemem;
- memmove(initrd, tmp, initrd_size);
- freemem += initrd_size;
- }
+ freemem_push_fdt(&freemem, fdt);
+ freemem_push_dt_initrd(&freemem);
memregions_init(arm_mem_regions, NR_MEM_REGIONS);
memregions_add_dt_regions(MAX_DT_MEM_REGIONS);
@@ -229,12 +263,7 @@
assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
setup_args_progname(bootargs);
- if (initrd) {
- /* environ is currently the only file in the initrd */
- char *env = malloc(initrd_size);
- memcpy(env, initrd, initrd_size);
- setup_env(env, initrd_size);
- }
+ initrd_setup();
if (!(auxinfo.flags & AUXINFO_MMU_OFF))
setup_vm();
@@ -266,112 +295,43 @@
static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo)
{
- int i;
- unsigned long free_mem_pages = 0;
- unsigned long free_mem_start = 0;
- struct efi_boot_memmap *map = &(efi_bootinfo->mem_map);
- efi_memory_desc_t *buffer = *map->map;
- efi_memory_desc_t *d = NULL;
- phys_addr_t base, top;
- struct mem_region r;
- uintptr_t text = (uintptr_t)&_text, etext = ALIGN((uintptr_t)&_etext, 4096);
- uintptr_t data = (uintptr_t)&_data, edata = ALIGN((uintptr_t)&_edata, 4096);
- const void *fdt = efi_bootinfo->fdt;
- int fdt_size, ret;
+ struct mem_region *freemem_mr = NULL, *code, *data;
+ phys_addr_t freemem_start;
+ void *freemem;
- /*
- * Record the largest free EFI_CONVENTIONAL_MEMORY region
- * which will be used to set up the memory allocator, so that
- * the memory allocator can work in the largest free
- * continuous memory region.
- */
- for (i = 0; i < *(map->map_size); i += *(map->desc_size)) {
- d = (efi_memory_desc_t *)(&((u8 *)buffer)[i]);
-
- r.start = d->phys_addr;
- r.end = d->phys_addr + d->num_pages * EFI_PAGE_SIZE;
- r.flags = 0;
-
- switch (d->type) {
- case EFI_RESERVED_TYPE:
- case EFI_LOADER_DATA:
- case EFI_BOOT_SERVICES_CODE:
- case EFI_BOOT_SERVICES_DATA:
- case EFI_RUNTIME_SERVICES_CODE:
- case EFI_RUNTIME_SERVICES_DATA:
- case EFI_UNUSABLE_MEMORY:
- case EFI_ACPI_RECLAIM_MEMORY:
- case EFI_ACPI_MEMORY_NVS:
- case EFI_PAL_CODE:
- r.flags = MR_F_RESERVED;
- break;
- case EFI_MEMORY_MAPPED_IO:
- case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
- r.flags = MR_F_IO;
- break;
- case EFI_LOADER_CODE:
- if (r.start <= text && r.end > text) {
- /* This is the unit test region. Flag the code separately. */
- phys_addr_t tmp = r.end;
-
- assert(etext <= data);
- assert(edata <= r.end);
- r.flags = MR_F_CODE;
- r.end = data;
- memregions_add(&r);
- r.start = data;
- r.end = tmp;
- r.flags = 0;
- } else {
- r.flags = MR_F_RESERVED;
- }
- break;
- case EFI_CONVENTIONAL_MEMORY:
- if (free_mem_pages < d->num_pages) {
- free_mem_pages = d->num_pages;
- free_mem_start = d->phys_addr;
- }
- break;
- }
-
- if (!(r.flags & MR_F_IO)) {
- if (r.start < __phys_offset)
- __phys_offset = r.start;
- if (r.end > __phys_end)
- __phys_end = r.end;
- }
- memregions_add(&r);
- }
- if (fdt) {
- /* Move the FDT to the base of free memory */
- fdt_size = fdt_totalsize(fdt);
- ret = fdt_move(fdt, (void *)free_mem_start, fdt_size);
- assert(ret == 0);
- ret = dt_init((void *)free_mem_start);
- assert(ret == 0);
- free_mem_start += ALIGN(fdt_size, EFI_PAGE_SIZE);
- free_mem_pages -= ALIGN(fdt_size, EFI_PAGE_SIZE) >> EFI_PAGE_SHIFT;
- }
-
- __phys_end &= PHYS_MASK;
- asm_mmu_disable();
-
- if (free_mem_pages == 0)
+ memregions_efi_init(&efi_bootinfo->mem_map, &freemem_mr);
+ if (!freemem_mr)
return EFI_OUT_OF_RESOURCES;
- assert(sizeof(long) == 8 || free_mem_start < (3ul << 30));
+ memregions_split((unsigned long)&_etext, &code, &data);
+ assert(code && (code->flags & MR_F_CODE));
+ if (data)
+ data->flags &= ~MR_F_CODE;
- phys_alloc_init(free_mem_start, free_mem_pages << EFI_PAGE_SHIFT);
- phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
+ for (struct mem_region *m = mem_regions; m->end; ++m) {
+ if (m != code)
+ assert(!(m->flags & MR_F_CODE));
- phys_alloc_get_unused(&base, &top);
- base = PAGE_ALIGN(base);
- top = top & PAGE_MASK;
- assert(sizeof(long) == 8 || !(base >> 32));
- if (sizeof(long) != 8 && (top >> 32) != 0)
- top = ((uint64_t)1 << 32);
- page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT);
- page_alloc_ops_enable();
+ if (!(m->flags & MR_F_IO)) {
+ if (m->start < __phys_offset)
+ __phys_offset = m->start;
+ if (m->end > __phys_end)
+ __phys_end = m->end;
+ }
+ }
+ __phys_end &= PHYS_MASK;
+
+ freemem = (void *)PAGE_ALIGN(freemem_mr->start);
+
+ if (efi_bootinfo->fdt)
+ freemem_push_fdt(&freemem, efi_bootinfo->fdt);
+
+ freemem_start = PAGE_ALIGN((unsigned long)freemem);
+ assert(sizeof(long) == 8 || freemem_start < (3ul << 30));
+
+ asm_mmu_disable();
+
+ mem_allocator_init(freemem_start, freemem_mr->end);
return EFI_SUCCESS;
}
@@ -380,10 +340,6 @@
{
efi_status_t status;
- struct thread_info *ti = current_thread_info();
-
- memset(ti, 0, sizeof(*ti));
-
exceptions_init();
memregions_init(arm_mem_regions, NR_MEM_REGIONS);
@@ -418,13 +374,8 @@
io_init();
timer_save_state();
- if (initrd) {
- /* environ is currently the only file in the initrd */
- char *env = malloc(initrd_size);
- memcpy(env, initrd, initrd_size);
- setup_env(env, initrd_size);
- }
+ initrd_setup();
if (!(auxinfo.flags & AUXINFO_MMU_OFF))
setup_vm();
diff --git a/lib/arm64/processor.c b/lib/arm64/processor.c
index 5bcad67..06fd7cf 100644
--- a/lib/arm64/processor.c
+++ b/lib/arm64/processor.c
@@ -130,9 +130,9 @@
printf("Vector: %d (%s)\n", v, vector_names[v]);
printf("ESR_EL1: %8s%08x, ec=%#x (%s)\n", "", esr, ec, ec_names[ec]);
printf("FAR_EL1: %016lx (%svalid)\n", far, far_valid ? "" : "not ");
- dump_stack();
printf("Exception frame registers:\n");
show_regs(regs);
+ dump_frame_stack((void *)regs->pc, (void *)regs->regs[29]);
abort();
}
diff --git a/lib/efi.c b/lib/efi.c
index d94f0fa..12c66c6 100644
--- a/lib/efi.c
+++ b/lib/efi.c
@@ -6,13 +6,17 @@
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
-
-#include "efi.h"
-#include <argv.h>
-#include <stdlib.h>
-#include <ctype.h>
#include <libcflat.h>
+#include <argv.h>
+#include <ctype.h>
+#include <stdlib.h>
#include <asm/setup.h>
+#include "efi.h"
+#include "libfdt/libfdt.h"
+
+/* From each arch */
+extern char *initrd;
+extern u32 initrd_size;
/* From lib/argv.c */
extern int __argc, __envc;
@@ -288,13 +292,77 @@
efi_char16_t var[] = ENV_VARNAME_DTBFILE;
efi_char16_t *val;
void *fdt = NULL;
- int fdtsize;
+ int fdtsize = 0;
val = efi_get_var(handle, image, var);
- if (val)
+ if (val) {
efi_load_image(handle, image, &fdt, &fdtsize, val);
+ if (fdtsize == 0)
+ return NULL;
+ } else if (efi_get_system_config_table(DEVICE_TREE_GUID, &fdt) != EFI_SUCCESS) {
+ return NULL;
+ }
- return fdt;
+ return fdt_check_header(fdt) == 0 ? fdt : NULL;
+}
+
+static const struct {
+ struct efi_vendor_dev_path vendor;
+ struct efi_generic_dev_path end;
+} __packed initrd_dev_path = {
+ {
+ {
+ EFI_DEV_MEDIA,
+ EFI_DEV_MEDIA_VENDOR,
+ sizeof(struct efi_vendor_dev_path),
+ },
+ LINUX_EFI_INITRD_MEDIA_GUID
+ }, {
+ EFI_DEV_END_PATH,
+ EFI_DEV_END_ENTIRE,
+ sizeof(struct efi_generic_dev_path)
+ }
+};
+
+static void efi_load_initrd(void)
+{
+ efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
+ efi_device_path_protocol_t *dp;
+ efi_load_file2_protocol_t *lf2;
+ efi_handle_t handle;
+ efi_status_t status;
+ unsigned long file_size = 0;
+
+ initrd = NULL;
+ initrd_size = 0;
+
+ dp = (efi_device_path_protocol_t *)&initrd_dev_path;
+ status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle);
+ if (status != EFI_SUCCESS)
+ return;
+
+ status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, (void **)&lf2);
+ assert(status == EFI_SUCCESS);
+
+ status = efi_call_proto(lf2, load_file, dp, false, &file_size, NULL);
+ assert(status == EFI_BUFFER_TOO_SMALL);
+
+ status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, file_size, (void **)&initrd);
+ assert(status == EFI_SUCCESS);
+
+ status = efi_call_proto(lf2, load_file, dp, false, &file_size, (void *)initrd);
+ assert(status == EFI_SUCCESS);
+
+ initrd_size = (u32)file_size;
+
+ /*
+ * UEFI appends initrd=initrd to the command line when an initrd is present.
+ * Remove it in order to avoid confusing unit tests.
+ */
+ if (!strcmp(__argv[__argc - 1], "initrd=initrd")) {
+ __argv[__argc - 1] = NULL;
+ __argc -= 1;
+ }
}
efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
@@ -335,6 +403,8 @@
}
setup_args(cmdline_ptr);
+ efi_load_initrd();
+
efi_bootinfo.fdt = efi_get_fdt(handle, image);
/* Set up efi_bootinfo */
efi_bootinfo.mem_map.map = ↦
diff --git a/lib/linux/efi.h b/lib/linux/efi.h
index 410f0b1..8fa23ad 100644
--- a/lib/linux/efi.h
+++ b/lib/linux/efi.h
@@ -66,8 +66,13 @@
#define ACPI_TABLE_GUID EFI_GUID(0xeb9d2d30, 0x2d88, 0x11d3, 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
#define ACPI_20_TABLE_GUID EFI_GUID(0x8868e871, 0xe4f1, 0x11d3, 0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81)
+#define DEVICE_TREE_GUID EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0)
+
#define LOADED_IMAGE_PROTOCOL_GUID EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+#define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
+#define LINUX_EFI_INITRD_MEDIA_GUID EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68)
+
typedef struct {
efi_guid_t guid;
void *table;
@@ -246,6 +251,12 @@
u16 length;
} __packed;
+struct efi_vendor_dev_path {
+ struct efi_generic_dev_path header;
+ efi_guid_t vendorguid;
+ u8 vendordata[];
+} __packed;
+
typedef struct efi_generic_dev_path efi_device_path_protocol_t;
/*
@@ -447,6 +458,19 @@
typedef struct _efi_file_protocol efi_file_protocol_t;
typedef efi_simple_file_system_protocol_t efi_file_io_interface_t;
typedef efi_file_protocol_t efi_file_t;
+typedef union efi_load_file_protocol efi_load_file_protocol_t;
+typedef union efi_load_file_protocol efi_load_file2_protocol_t;
+
+union efi_load_file_protocol {
+ struct {
+ efi_status_t (__efiapi *load_file)(efi_load_file_protocol_t *,
+ efi_device_path_protocol_t *,
+ bool, unsigned long *, void *);
+ };
+ struct {
+ u32 load_file;
+ } mixed_mode;
+};
typedef efi_status_t efi_simple_file_system_protocol_open_volume(
efi_simple_file_system_protocol_t *this,
@@ -542,7 +566,12 @@
efi_char16_t file_name[1];
} efi_file_info_t;
+#define efi_fn_call(inst, func, ...) (inst)->func(__VA_ARGS__)
#define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__)
#define efi_rs_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__)
+#define efi_call_proto(inst, func, ...) ({ \
+ __typeof__(inst) __inst = (inst); \
+ efi_fn_call(__inst, func, __inst, ##__VA_ARGS__); \
+})
#endif /* __LINUX_UEFI_H */
diff --git a/lib/memregions.c b/lib/memregions.c
index 96de86b..db7522c 100644
--- a/lib/memregions.c
+++ b/lib/memregions.c
@@ -80,3 +80,57 @@
});
}
}
+
+#ifdef CONFIG_EFI
+/*
+ * Add memory regions based on the EFI memory map. Also set a pointer to the
+ * memory region which corresponds to the largest EFI_CONVENTIONAL_MEMORY
+ * region, as that region is the largest free, continuous region, making it
+ * a good choice for the memory allocator.
+ */
+void memregions_efi_init(struct efi_boot_memmap *mem_map,
+ struct mem_region **freemem)
+{
+ u8 *buffer = (u8 *)*mem_map->map;
+ u64 freemem_pages = 0;
+
+ *freemem = NULL;
+
+ for (int i = 0; i < *mem_map->map_size; i += *mem_map->desc_size) {
+ efi_memory_desc_t *d = (efi_memory_desc_t *)&buffer[i];
+ struct mem_region r = {
+ .start = d->phys_addr,
+ .end = d->phys_addr + d->num_pages * EFI_PAGE_SIZE,
+ .flags = 0,
+ };
+
+ switch (d->type) {
+ case EFI_MEMORY_MAPPED_IO:
+ case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+ r.flags = MR_F_IO;
+ break;
+ case EFI_LOADER_CODE:
+ r.flags = MR_F_CODE;
+ break;
+ case EFI_LOADER_DATA:
+ case EFI_ACPI_RECLAIM_MEMORY:
+ break;
+ case EFI_PERSISTENT_MEMORY:
+ r.flags = MR_F_PERSISTENT;
+ break;
+ case EFI_CONVENTIONAL_MEMORY:
+ if (freemem_pages < d->num_pages) {
+ freemem_pages = d->num_pages;
+ *freemem = memregions_add(&r);
+ continue;
+ }
+ break;
+ default:
+ r.flags = MR_F_RESERVED;
+ break;
+ }
+
+ memregions_add(&r);
+ }
+}
+#endif /* CONFIG_EFI */
diff --git a/lib/memregions.h b/lib/memregions.h
index 9a8e331..1600530 100644
--- a/lib/memregions.h
+++ b/lib/memregions.h
@@ -9,6 +9,7 @@
#define MR_F_IO BIT(0)
#define MR_F_CODE BIT(1)
#define MR_F_RESERVED BIT(2)
+#define MR_F_PERSISTENT BIT(3)
#define MR_F_UNKNOWN BIT(31)
struct mem_region {
@@ -26,4 +27,9 @@
void memregions_split(phys_addr_t addr, struct mem_region **r1, struct mem_region **r2);
void memregions_add_dt_regions(size_t max_nr);
+#ifdef CONFIG_EFI
+#include <efi.h>
+void memregions_efi_init(struct efi_boot_memmap *mem_map, struct mem_region **freemem);
+#endif
+
#endif /* _MEMREGIONS_H_ */
diff --git a/run_tests.sh b/run_tests.sh
index abb0ab7..bb3024f 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -44,7 +44,7 @@
only_tests=""
list_tests=""
-args=$(getopt -u -o ag:htj:vl -l all,group:,help,tap13,parallel:,verbose,list -- $*)
+args=$(getopt -u -o ag:htj:vl -l all,group:,help,tap13,parallel:,verbose,list,probe-maxsmp -- $*)
[ $? -ne 0 ] && exit 2;
set -- $args;
while [ $# -gt 0 ]; do
@@ -78,6 +78,9 @@
-l | --list)
list_tests="yes"
;;
+ --probe-maxsmp)
+ probe_maxsmp
+ ;;
--)
;;
*)
diff --git a/scripts/runtime.bash b/scripts/runtime.bash
index c73fb02..255e756 100644
--- a/scripts/runtime.bash
+++ b/scripts/runtime.bash
@@ -18,7 +18,7 @@
local log="$(eval "$(get_cmdline _NO_FILE_4Uhere_)" 2>&1)"
echo "$log" | grep "_NO_FILE_4Uhere_" |
- grep -q -e "could not \(load\|open\) kernel" -e "error loading" &&
+ grep -q -e "could not \(load\|open\) kernel" -e "error loading" -e "failed to load" &&
return 1
RUNTIME_log_stderr <<< "$log"
@@ -200,12 +200,13 @@
#
# Probe for MAX_SMP, in case it's less than the number of host cpus.
#
-# This probing currently only works for ARM, as x86 bails on another
-# error first, so this check is only run for ARM and ARM64. The
-# parameter expansion takes the last number from the QEMU error
-# message, which gives the allowable MAX_SMP.
-if [[ $ARCH == 'arm' || $ARCH == 'arm64' ]] &&
- smp=$($RUNTIME_arch_run _NO_FILE_4Uhere_ -smp $MAX_SMP |& grep 'exceeds max CPUs'); then
- smp=${smp##*(}
- MAX_SMP=${smp:0:-1}
-fi
+function probe_maxsmp()
+{
+ local smp
+
+ if smp=$($RUNTIME_arch_run _NO_FILE_4Uhere_ -smp $MAX_SMP |& grep 'Invalid SMP CPUs'); then
+ smp=${smp##* }
+ echo "Restricting MAX_SMP from ($MAX_SMP) to the max supported ($smp)" >&2
+ MAX_SMP=$smp
+ fi
+}