summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv/Kconfig46
-rw-r--r--arch/riscv/cpu/ax25/Kconfig7
-rw-r--r--arch/riscv/cpu/cpu.c9
-rw-r--r--arch/riscv/cpu/start.S167
-rw-r--r--arch/riscv/dts/Makefile2
-rw-r--r--arch/riscv/dts/ae350_32.dts81
-rw-r--r--arch/riscv/dts/ae350_64.dts81
-rw-r--r--arch/riscv/include/asm/csr.h1
-rw-r--r--arch/riscv/include/asm/global_data.h12
-rw-r--r--arch/riscv/include/asm/sbi.h94
-rw-r--r--arch/riscv/include/asm/smp.h53
-rw-r--r--arch/riscv/include/asm/syscon.h4
-rw-r--r--arch/riscv/lib/Makefile4
-rw-r--r--arch/riscv/lib/andes_plic.c113
-rw-r--r--arch/riscv/lib/andes_plmt.c53
-rw-r--r--arch/riscv/lib/asm-offsets.c1
-rw-r--r--arch/riscv/lib/bootm.c13
-rw-r--r--arch/riscv/lib/sbi_ipi.c25
-rw-r--r--arch/riscv/lib/smp.c118
-rw-r--r--board/AndesTech/ax25-ae350/Kconfig1
-rw-r--r--board/emulation/qemu-riscv/Kconfig1
-rw-r--r--board/sifive/fu540/Kconfig1
-rw-r--r--configs/ae350_rv32_defconfig1
-rw-r--r--configs/ae350_rv64_defconfig1
-rw-r--r--include/efi_api.h4
-rw-r--r--include/efi_loader.h14
-rw-r--r--lib/efi_loader/efi_bootmgr.c51
-rw-r--r--lib/efi_loader/efi_boottime.c328
-rw-r--r--lib/efi_loader/efi_console.c17
-rw-r--r--lib/efi_loader/efi_file.c108
-rw-r--r--lib/efi_loader/efi_image_loader.c24
-rw-r--r--lib/efi_loader/efi_memory.c80
-rw-r--r--lib/efi_loader/efi_setup.c27
-rw-r--r--lib/efi_loader/efi_variable.c10
-rw-r--r--lib/efi_selftest/efi_selftest_miniapp_exit.c58
-rw-r--r--test/py/tests/test_efi_selftest.py2
36 files changed, 1335 insertions, 277 deletions
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 36512a89950..ae8ff7b7657 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -109,6 +109,24 @@ config SIFIVE_CLINT
The SiFive CLINT block holds memory-mapped control and status registers
associated with software and timer interrupts.
+config ANDES_PLIC
+ bool
+ depends on RISCV_MMODE
+ select REGMAP
+ select SYSCON
+ help
+ The Andes PLIC block holds memory-mapped claim and pending registers
+ associated with software interrupt.
+
+config ANDES_PLMT
+ bool
+ depends on RISCV_MMODE
+ select REGMAP
+ select SYSCON
+ help
+ The Andes PLMT block holds memory-mapped mtime register
+ associated with timer tick.
+
config RISCV_RDTIME
bool
default y if RISCV_SMODE
@@ -120,4 +138,32 @@ config RISCV_RDTIME
config SYS_MALLOC_F_LEN
default 0x1000
+config SMP
+ bool "Symmetric Multi-Processing"
+ help
+ This enables support for systems with more than one CPU. If
+ you say N here, U-Boot will run on single and multiprocessor
+ machines, but will use only one CPU of a multiprocessor
+ machine. If you say Y here, U-Boot will run on many, but not
+ all, single processor machines.
+
+config NR_CPUS
+ int "Maximum number of CPUs (2-32)"
+ range 2 32
+ depends on SMP
+ default 8
+ help
+ On multiprocessor machines, U-Boot sets up a stack for each CPU.
+ Stack memory is pre-allocated. U-Boot must therefore know the
+ maximum number of CPUs that may be present.
+
+config SBI_IPI
+ bool
+ default y if RISCV_SMODE
+ depends on SMP
+
+config STACK_SIZE_SHIFT
+ int
+ default 13
+
endmenu
diff --git a/arch/riscv/cpu/ax25/Kconfig b/arch/riscv/cpu/ax25/Kconfig
index e9dbca2faea..6b4b92e6921 100644
--- a/arch/riscv/cpu/ax25/Kconfig
+++ b/arch/riscv/cpu/ax25/Kconfig
@@ -1,5 +1,11 @@
config RISCV_NDS
bool
+ select ARCH_EARLY_INIT_R
+ imply CPU
+ imply CPU_RISCV
+ imply RISCV_TIMER
+ imply ANDES_PLIC if RISCV_MMODE
+ imply ANDES_PLMT if RISCV_MMODE
help
Run U-Boot on AndeStar V5 platforms and use some specific features
which are provided by Andes Technology AndeStar V5 families.
@@ -8,6 +14,7 @@ if RISCV_NDS
config RISCV_NDS_CACHE
bool "AndeStar V5 families specific cache support"
+ depends on RISCV_MMODE
help
Provide Andes Technology AndeStar V5 families specific cache support.
diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
index e662140427a..c32de8a4c3e 100644
--- a/arch/riscv/cpu/cpu.c
+++ b/arch/riscv/cpu/cpu.c
@@ -12,10 +12,17 @@
#include <dm/uclass-internal.h>
/*
- * prior_stage_fdt_address must be stored in the data section since it is used
+ * The variables here must be stored in the data section since they are used
* before the bss section is available.
*/
phys_addr_t prior_stage_fdt_address __attribute__((section(".data")));
+u32 hart_lottery __attribute__((section(".data"))) = 0;
+
+/*
+ * The main hart running U-Boot has acquired available_harts_lock until it has
+ * finished initialization of global data.
+ */
+u32 available_harts_lock = 1;
static inline bool supports_extension(char ext)
{
diff --git a/arch/riscv/cpu/start.S b/arch/riscv/cpu/start.S
index 81ea52b170f..a4433fbd6ba 100644
--- a/arch/riscv/cpu/start.S
+++ b/arch/riscv/cpu/start.S
@@ -13,6 +13,7 @@
#include <config.h>
#include <common.h>
#include <elf.h>
+#include <asm/csr.h>
#include <asm/encoding.h>
#include <generated/asm-offsets.h>
@@ -32,11 +33,19 @@
#define SYM_SIZE 0x18
#endif
+.section .data
+secondary_harts_relocation_error:
+ .ascii "Relocation of secondary harts has failed, error %d\n"
+
.section .text
.globl _start
_start:
+#ifdef CONFIG_RISCV_MMODE
+ csrr a0, mhartid
+#endif
+
/* save hart id and dtb pointer */
- mv s0, a0
+ mv tp, a0
mv s1, a1
la t0, trap_entry
@@ -45,9 +54,22 @@ _start:
/* mask all interrupts */
csrw MODE_PREFIX(ie), zero
- /* Enable cache */
- jal icache_enable
- jal dcache_enable
+#ifdef CONFIG_SMP
+ /* check if hart is within range */
+ /* tp: hart id */
+ li t0, CONFIG_NR_CPUS
+ bge tp, t0, hart_out_of_bounds_loop
+#endif
+
+#ifdef CONFIG_SMP
+ /* set xSIE bit to receive IPIs */
+#ifdef CONFIG_RISCV_MMODE
+ li t0, MIE_MSIE
+#else
+ li t0, SIE_SSIE
+#endif
+ csrs MODE_PREFIX(ie), t0
+#endif
/*
* Set stackpointer in internal/ex RAM to call board_init_f
@@ -57,14 +79,33 @@ call_board_init_f:
li t1, CONFIG_SYS_INIT_SP_ADDR
and sp, t1, t0 /* force 16 byte alignment */
-#ifdef CONFIG_DEBUG_UART
- jal debug_uart_init
-#endif
-
call_board_init_f_0:
mv a0, sp
jal board_init_f_alloc_reserve
+
+ /*
+ * Set global data pointer here for all harts, uninitialized at this
+ * point.
+ */
+ mv gp, a0
+
+ /* setup stack */
+#ifdef CONFIG_SMP
+ /* tp: hart id */
+ slli t0, tp, CONFIG_STACK_SIZE_SHIFT
+ sub sp, a0, t0
+#else
mv sp, a0
+#endif
+
+ /*
+ * Pick hart to initialize global data and run U-Boot. The other harts
+ * wait for initialization to complete.
+ */
+ la t0, hart_lottery
+ li s2, 1
+ amoswap.w s2, t1, 0(t0)
+ bnez s2, wait_for_gd_init
la t0, prior_stage_fdt_address
SREG s1, 0(t0)
@@ -72,7 +113,42 @@ call_board_init_f_0:
jal board_init_f_init_reserve
/* save the boot hart id to global_data */
- SREG s0, GD_BOOT_HART(gp)
+ SREG tp, GD_BOOT_HART(gp)
+
+ la t0, available_harts_lock
+ fence rw, w
+ amoswap.w zero, zero, 0(t0)
+
+wait_for_gd_init:
+ la t0, available_harts_lock
+ li t1, 1
+1: amoswap.w t1, t1, 0(t0)
+ fence r, rw
+ bnez t1, 1b
+
+ /* register available harts in the available_harts mask */
+ li t1, 1
+ sll t1, t1, tp
+ LREG t2, GD_AVAILABLE_HARTS(gp)
+ or t2, t2, t1
+ SREG t2, GD_AVAILABLE_HARTS(gp)
+
+ fence rw, w
+ amoswap.w zero, zero, 0(t0)
+
+ /*
+ * Continue on hart lottery winner, others branch to
+ * secondary_hart_loop.
+ */
+ bnez s2, secondary_hart_loop
+
+ /* Enable cache */
+ jal icache_enable
+ jal dcache_enable
+
+#ifdef CONFIG_DEBUG_UART
+ jal debug_uart_init
+#endif
mv a0, zero /* a0 <-- boot_flags = 0 */
la t5, board_init_f
@@ -95,7 +171,14 @@ relocate_code:
*Set up the stack
*/
stack_setup:
+#ifdef CONFIG_SMP
+ /* tp: hart id */
+ slli t0, tp, CONFIG_STACK_SIZE_SHIFT
+ sub sp, s2, t0
+#else
mv sp, s2
+#endif
+
la t0, _start
sub t6, s4, t0 /* t6 <- relocation offset */
beq t0, s4, clear_bss /* skip relocation */
@@ -175,13 +258,37 @@ clear_bss:
add t0, t0, t6 /* t0 <- rel __bss_start in RAM */
la t1, __bss_end /* t1 <- rel __bss_end in FLASH */
add t1, t1, t6 /* t1 <- rel __bss_end in RAM */
- beq t0, t1, call_board_init_r
+ beq t0, t1, relocate_secondary_harts
clbss_l:
SREG zero, 0(t0) /* clear loop... */
addi t0, t0, REGBYTES
bne t0, t1, clbss_l
+relocate_secondary_harts:
+#ifdef CONFIG_SMP
+ /* send relocation IPI */
+ la t0, secondary_hart_relocate
+ add a0, t0, t6
+
+ /* store relocation offset */
+ mv s5, t6
+
+ mv a1, s2
+ mv a2, s3
+ jal smp_call_function
+
+ /* hang if relocation of secondary harts has failed */
+ beqz a0, 1f
+ mv a1, a0
+ la a0, secondary_harts_relocation_error
+ jal printf
+ jal hang
+
+ /* restore relocation offset */
+1: mv t6, s5
+#endif
+
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
@@ -202,3 +309,43 @@ call_board_init_r:
* jump to it ...
*/
jr t4 /* jump to board_init_r() */
+
+#ifdef CONFIG_SMP
+hart_out_of_bounds_loop:
+ /* Harts in this loop are out of bounds, increase CONFIG_NR_CPUS. */
+ wfi
+ j hart_out_of_bounds_loop
+#endif
+
+#ifdef CONFIG_SMP
+/* SMP relocation entry */
+secondary_hart_relocate:
+ /* a1: new sp */
+ /* a2: new gd */
+ /* tp: hart id */
+
+ /* setup stack */
+ slli t0, tp, CONFIG_STACK_SIZE_SHIFT
+ sub sp, a1, t0
+
+ /* update global data pointer */
+ mv gp, a2
+#endif
+
+secondary_hart_loop:
+ wfi
+
+#ifdef CONFIG_SMP
+ csrr t0, MODE_PREFIX(ip)
+#ifdef CONFIG_RISCV_MMODE
+ andi t0, t0, MIE_MSIE
+#else
+ andi t0, t0, SIE_SSIE
+#endif
+ beqz t0, secondary_hart_loop
+
+ mv a0, tp
+ jal handle_ipi
+#endif
+
+ j secondary_hart_loop
diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile
index b400defb380..f9cd606a9aa 100644
--- a/arch/riscv/dts/Makefile
+++ b/arch/riscv/dts/Makefile
@@ -1,5 +1,7 @@
# SPDX-License-Identifier: GPL-2.0+
+dtb-$(CONFIG_TARGET_AX25_AE350) += ae350_32.dtb ae350_64.dtb
+
targets += $(dtb-y)
DTC_FLAGS += -R 4 -p 0x1000
diff --git a/arch/riscv/dts/ae350_32.dts b/arch/riscv/dts/ae350_32.dts
index 06798273138..2ec01a5ce79 100644
--- a/arch/riscv/dts/ae350_32.dts
+++ b/arch/riscv/dts/ae350_32.dts
@@ -26,16 +26,49 @@
status = "okay";
compatible = "riscv";
riscv,isa = "rv32imafdc";
+ riscv,priv-major = <1>;
+ riscv,priv-minor = <10>;
mmu-type = "riscv,sv32";
clock-frequency = <60000000>;
+ i-cache-size = <0x8000>;
+ i-cache-line-size = <32>;
d-cache-size = <0x8000>;
d-cache-line-size = <32>;
+ next-level-cache = <&L2>;
CPU0_intc: interrupt-controller {
#interrupt-cells = <1>;
interrupt-controller;
compatible = "riscv,cpu-intc";
};
};
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ reg = <1>;
+ status = "okay";
+ compatible = "riscv";
+ riscv,isa = "rv32imafdc";
+ riscv,priv-major = <1>;
+ riscv,priv-minor = <10>;
+ mmu-type = "riscv,sv32";
+ clock-frequency = <60000000>;
+ i-cache-size = <0x8000>;
+ i-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-line-size = <32>;
+ next-level-cache = <&L2>;
+ CPU1_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ compatible = "riscv,cpu-intc";
+ };
+ };
+
+ L2: l2-cache@e0500000 {
+ compatible = "cache";
+ cache-level = <2>;
+ cache-size = <0x40000>;
+ reg = <0x0 0xe0500000 0x0 0x40000>;
+ };
};
memory@0 {
@@ -46,32 +79,32 @@
soc {
#address-cells = <1>;
#size-cells = <1>;
- compatible = "andestech,riscv-ae350-soc";
+ compatible = "simple-bus";
ranges;
- plic0: interrupt-controller@e4000000 {
- compatible = "riscv,plic0";
- #address-cells = <1>;
- #interrupt-cells = <1>;
- interrupt-controller;
- reg = <0xe4000000 0x2000000>;
- riscv,ndev=<71>;
- interrupts-extended = <&CPU0_intc 11 &CPU0_intc 9>;
- };
+ plic0: interrupt-controller@e4000000 {
+ compatible = "riscv,plic0";
+ #address-cells = <1>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ reg = <0xe4000000 0x2000000>;
+ riscv,ndev=<71>;
+ interrupts-extended = <&CPU0_intc 11 &CPU0_intc 9 &CPU1_intc 11 &CPU1_intc 9>;
+ };
- plic1: interrupt-controller@e6400000 {
- compatible = "riscv,plic1";
- #address-cells = <1>;
- #interrupt-cells = <1>;
- interrupt-controller;
- reg = <0xe6400000 0x400000>;
- riscv,ndev=<1>;
- interrupts-extended = <&CPU0_intc 3>;
- };
+ plic1: interrupt-controller@e6400000 {
+ compatible = "riscv,plic1";
+ #address-cells = <1>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ reg = <0xe6400000 0x400000>;
+ riscv,ndev=<2>;
+ interrupts-extended = <&CPU0_intc 3 &CPU1_intc 3>;
+ };
- plmt0@e6000000 {
- compatible = "riscv,plmt0";
- interrupts-extended = <&CPU0_intc 7>;
+ plmt0@e6000000 {
+ compatible = "riscv,plmt0";
+ interrupts-extended = <&CPU0_intc 7 &CPU1_intc 7>;
reg = <0xe6000000 0x100000>;
};
};
@@ -146,6 +179,10 @@
interrupt-parent = <&plic0>;
};
+ pmu {
+ compatible = "riscv,base-pmu";
+ };
+
virtio_mmio@fe007000 {
interrupts = <0x17 0x4>;
interrupt-parent = <0x2>;
diff --git a/arch/riscv/dts/ae350_64.dts b/arch/riscv/dts/ae350_64.dts
index e48c2986452..cde5cdeff83 100644
--- a/arch/riscv/dts/ae350_64.dts
+++ b/arch/riscv/dts/ae350_64.dts
@@ -26,16 +26,49 @@
status = "okay";
compatible = "riscv";
riscv,isa = "rv64imafdc";
+ riscv,priv-major = <1>;
+ riscv,priv-minor = <10>;
mmu-type = "riscv,sv39";
clock-frequency = <60000000>;
+ i-cache-size = <0x8000>;
+ i-cache-line-size = <32>;
d-cache-size = <0x8000>;
d-cache-line-size = <32>;
+ next-level-cache = <&L2>;
CPU0_intc: interrupt-controller {
#interrupt-cells = <1>;
interrupt-controller;
compatible = "riscv,cpu-intc";
};
};
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ reg = <1>;
+ status = "okay";
+ compatible = "riscv";
+ riscv,isa = "rv64imafdc";
+ riscv,priv-major = <1>;
+ riscv,priv-minor = <10>;
+ mmu-type = "riscv,sv39";
+ clock-frequency = <60000000>;
+ i-cache-size = <0x8000>;
+ i-cache-line-size = <32>;
+ d-cache-size = <0x8000>;
+ d-cache-line-size = <32>;
+ next-level-cache = <&L2>;
+ CPU1_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ compatible = "riscv,cpu-intc";
+ };
+ };
+
+ L2: l2-cache@e0500000 {
+ compatible = "cache";
+ cache-level = <2>;
+ cache-size = <0x40000>;
+ reg = <0x0 0xe0500000 0x0 0x40000>;
+ };
};
memory@0 {
@@ -46,32 +79,32 @@
soc {
#address-cells = <2>;
#size-cells = <2>;
- compatible = "andestech,riscv-ae350-soc";
+ compatible = "simple-bus";
ranges;
- plic0: interrupt-controller@e4000000 {
- compatible = "riscv,plic0";
- #address-cells = <2>;
- #interrupt-cells = <2>;
- interrupt-controller;
- reg = <0x0 0xe4000000 0x0 0x2000000>;
- riscv,ndev=<71>;
- interrupts-extended = <&CPU0_intc 11 &CPU0_intc 9>;
- };
+ plic0: interrupt-controller@e4000000 {
+ compatible = "riscv,plic0";
+ #address-cells = <2>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0x0 0xe4000000 0x0 0x2000000>;
+ riscv,ndev=<71>;
+ interrupts-extended = <&CPU0_intc 11 &CPU0_intc 9 &CPU1_intc 11 &CPU1_intc 9>;
+ };
- plic1: interrupt-controller@e6400000 {
- compatible = "riscv,plic1";
- #address-cells = <2>;
- #interrupt-cells = <2>;
- interrupt-controller;
- reg = <0x0 0xe6400000 0x0 0x400000>;
- riscv,ndev=<1>;
- interrupts-extended = <&CPU0_intc 3>;
- };
+ plic1: interrupt-controller@e6400000 {
+ compatible = "riscv,plic1";
+ #address-cells = <2>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0x0 0xe6400000 0x0 0x400000>;
+ riscv,ndev=<2>;
+ interrupts-extended = <&CPU0_intc 3 &CPU1_intc 3>;
+ };
- plmt0@e6000000 {
- compatible = "riscv,plmt0";
- interrupts-extended = <&CPU0_intc 7>;
+ plmt0@e6000000 {
+ compatible = "riscv,plmt0";
+ interrupts-extended = <&CPU0_intc 7 &CPU1_intc 7>;
reg = <0x0 0xe6000000 0x0 0x100000>;
};
};
@@ -146,6 +179,10 @@
interrupt-parent = <&plic0>;
};
+ pmu {
+ compatible = "riscv,base-pmu";
+ };
+
virtio_mmio@fe007000 {
interrupts = <0x17 0x4>;
interrupt-parent = <0x2>;
diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index 86136f542c7..644e6baa157 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -46,6 +46,7 @@
#endif
/* Interrupt Enable and Interrupt Pending flags */
+#define MIE_MSIE _AC(0x00000008, UL) /* Software Interrupt Enable */
#define SIE_SSIE _AC(0x00000002, UL) /* Software Interrupt Enable */
#define SIE_STIE _AC(0x00000020, UL) /* Timer Interrupt Enable */
diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
index a3a342c6e1c..dffcd45bf01 100644
--- a/arch/riscv/include/asm/global_data.h
+++ b/arch/riscv/include/asm/global_data.h
@@ -10,12 +10,24 @@
#ifndef __ASM_GBL_DATA_H
#define __ASM_GBL_DATA_H
+#include <asm/smp.h>
+
/* Architecture-specific global data */
struct arch_global_data {
long boot_hart; /* boot hart id */
#ifdef CONFIG_SIFIVE_CLINT
void __iomem *clint; /* clint base address */
#endif
+#ifdef CONFIG_ANDES_PLIC
+ void __iomem *plic; /* plic base address */
+#endif
+#ifdef CONFIG_ANDES_PLMT
+ void __iomem *plmt; /* plmt base address */
+#endif
+#ifdef CONFIG_SMP
+ struct ipi_data ipi[CONFIG_NR_CPUS];
+#endif
+ ulong available_harts;
};
#include <asm-generic/global_data.h>
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
new file mode 100644
index 00000000000..ced57defdd3
--- /dev/null
+++ b/arch/riscv/include/asm/sbi.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2015 Regents of the University of California
+ *
+ * Taken from Linux arch/riscv/include/asm/sbi.h
+ */
+
+#ifndef _ASM_RISCV_SBI_H
+#define _ASM_RISCV_SBI_H
+
+#include <linux/types.h>
+
+#define SBI_SET_TIMER 0
+#define SBI_CONSOLE_PUTCHAR 1
+#define SBI_CONSOLE_GETCHAR 2
+#define SBI_CLEAR_IPI 3
+#define SBI_SEND_IPI 4
+#define SBI_REMOTE_FENCE_I 5
+#define SBI_REMOTE_SFENCE_VMA 6
+#define SBI_REMOTE_SFENCE_VMA_ASID 7
+#define SBI_SHUTDOWN 8
+
+#define SBI_CALL(which, arg0, arg1, arg2) ({ \
+ register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \
+ register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \
+ register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \
+ register uintptr_t a7 asm ("a7") = (uintptr_t)(which); \
+ asm volatile ("ecall" \
+ : "+r" (a0) \
+ : "r" (a1), "r" (a2), "r" (a7) \
+ : "memory"); \
+ a0; \
+})
+
+/* Lazy implementations until SBI is finalized */
+#define SBI_CALL_0(which) SBI_CALL(which, 0, 0, 0)
+#define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0)
+#define SBI_CALL_2(which, arg0, arg1) SBI_CALL(which, arg0, arg1, 0)
+
+static inline void sbi_console_putchar(int ch)
+{
+ SBI_CALL_1(SBI_CONSOLE_PUTCHAR, ch);
+}
+
+static inline int sbi_console_getchar(void)
+{
+ return SBI_CALL_0(SBI_CONSOLE_GETCHAR);
+}
+
+static inline void sbi_set_timer(uint64_t stime_value)
+{
+#if __riscv_xlen == 32
+ SBI_CALL_2(SBI_SET_TIMER, stime_value, stime_value >> 32);
+#else
+ SBI_CALL_1(SBI_SET_TIMER, stime_value);
+#endif
+}
+
+static inline void sbi_shutdown(void)
+{
+ SBI_CALL_0(SBI_SHUTDOWN);
+}
+
+static inline void sbi_clear_ipi(void)
+{
+ SBI_CALL_0(SBI_CLEAR_IPI);
+}
+
+static inline void sbi_send_ipi(const unsigned long *hart_mask)
+{
+ SBI_CALL_1(SBI_SEND_IPI, hart_mask);
+}
+
+static inline void sbi_remote_fence_i(const unsigned long *hart_mask)
+{
+ SBI_CALL_1(SBI_REMOTE_FENCE_I, hart_mask);
+}
+
+static inline void sbi_remote_sfence_vma(const unsigned long *hart_mask,
+ unsigned long start,
+ unsigned long size)
+{
+ SBI_CALL_1(SBI_REMOTE_SFENCE_VMA, hart_mask);
+}
+
+static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
+ unsigned long start,
+ unsigned long size,
+ unsigned long asid)
+{
+ SBI_CALL_1(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask);
+}
+
+#endif
diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
new file mode 100644
index 00000000000..bc863fdbafb
--- /dev/null
+++ b/arch/riscv/include/asm/smp.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Fraunhofer AISEC,
+ * Lukas Auer <lukas.auer@aisec.fraunhofer.de>
+ */
+
+#ifndef _ASM_RISCV_SMP_H
+#define _ASM_RISCV_SMP_H
+
+/**
+ * struct ipi_data - Inter-processor interrupt (IPI) data structure
+ *
+ * IPIs are used for SMP support to communicate to other harts what function to
+ * call. Functions are in the form
+ * void (*addr)(ulong hart, ulong arg0, ulong arg1).
+ *
+ * The function address and the two arguments, arg0 and arg1, are stored in the
+ * IPI data structure. The hart ID is inserted by the hart handling the IPI and
+ * calling the function.
+ *
+ * @addr: Address of function
+ * @arg0: First argument of function
+ * @arg1: Second argument of function
+ */
+struct ipi_data {
+ ulong addr;
+ ulong arg0;
+ ulong arg1;
+};
+
+/**
+ * handle_ipi() - interrupt handler for software interrupts
+ *
+ * The IPI interrupt handler must be called to handle software interrupts. It
+ * calls the function specified in the hart's IPI data structure.
+ *
+ * @hart: Hart ID of the current hart
+ */
+void handle_ipi(ulong hart);
+
+/**
+ * smp_call_function() - Call a function on all other harts
+ *
+ * Send IPIs with the specified function call to all harts.
+ *
+ * @addr: Address of function
+ * @arg0: First argument of function
+ * @arg1: Second argument of function
+ * @return 0 if OK, -ve on error
+ */
+int smp_call_function(ulong addr, ulong arg0, ulong arg1);
+
+#endif
diff --git a/arch/riscv/include/asm/syscon.h b/arch/riscv/include/asm/syscon.h
index d311ee6b45f..26a008ca599 100644
--- a/arch/riscv/include/asm/syscon.h
+++ b/arch/riscv/include/asm/syscon.h
@@ -8,12 +8,12 @@
/*
* System controllers in a RISC-V system
- *
- * So far only SiFive's Core Local Interruptor (CLINT) is defined.
*/
enum {
RISCV_NONE,
RISCV_SYSCON_CLINT, /* Core Local Interruptor (CLINT) */
+ RISCV_SYSCON_PLIC, /* Platform Level Interrupt Controller (PLIC) */
+ RISCV_SYSCON_PLMT, /* Platform Level Machine Timer (PLMT) */
};
#endif /* _ASM_SYSCON_H */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index edfa61690c7..1c332db436a 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -11,9 +11,13 @@ obj-$(CONFIG_CMD_GO) += boot.o
obj-y += cache.o
obj-$(CONFIG_RISCV_RDTIME) += rdtime.o
obj-$(CONFIG_SIFIVE_CLINT) += sifive_clint.o
+obj-$(CONFIG_ANDES_PLIC) += andes_plic.o
+obj-$(CONFIG_ANDES_PLMT) += andes_plmt.o
obj-y += interrupts.o
obj-y += reset.o
+obj-$(CONFIG_SBI_IPI) += sbi_ipi.o
obj-y += setjmp.o
+obj-$(CONFIG_SMP) += smp.o
# For building EFI apps
CFLAGS_$(EFI_CRT0) := $(CFLAGS_EFI)
diff --git a/arch/riscv/lib/andes_plic.c b/arch/riscv/lib/andes_plic.c
new file mode 100644
index 00000000000..2ffe49ac901
--- /dev/null
+++ b/arch/riscv/lib/andes_plic.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019, Rick Chen <rick@andestech.com>
+ *
+ * U-Boot syscon driver for Andes's Platform Level Interrupt Controller (PLIC).
+ * The PLIC block holds memory-mapped claim and pending registers
+ * associated with software interrupt.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/syscon.h>
+#include <cpu.h>
+
+/* pending register */
+#define PENDING_REG(base, hart) ((ulong)(base) + 0x1000 + (hart) * 8)
+/* enable register */
+#define ENABLE_REG(base, hart) ((ulong)(base) + 0x2000 + (hart) * 0x80)
+/* claim register */
+#define CLAIM_REG(base, hart) ((ulong)(base) + 0x200004 + (hart) * 0x1000)
+
+#define ENABLE_HART_IPI (0x80808080)
+#define SEND_IPI_TO_HART(hart) (0x80 >> (hart))
+
+DECLARE_GLOBAL_DATA_PTR;
+static int init_plic(void);
+
+#define PLIC_BASE_GET(void) \
+ do { \
+ long *ret; \
+ \
+ if (!gd->arch.plic) { \
+ ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
+ if (IS_ERR(ret)) \
+ return PTR_ERR(ret); \
+ gd->arch.plic = ret; \
+ init_plic(); \
+ } \
+ } while (0)
+
+static int enable_ipi(int harts)
+{
+ int i;
+ int en = ENABLE_HART_IPI;
+
+ for (i = 0; i < harts; i++) {
+ en = en >> i;
+ writel(en, (void __iomem *)ENABLE_REG(gd->arch.plic, i));
+ }
+
+ return 0;
+}
+
+static int init_plic(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_find_first_device(UCLASS_CPU, &dev);
+ if (ret)
+ return ret;
+
+ if (ret == 0 && dev) {
+ ret = cpu_get_count(dev);
+ if (ret < 0)
+ return ret;
+
+ enable_ipi(ret);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+int riscv_send_ipi(int hart)
+{
+ PLIC_BASE_GET();
+
+ writel(SEND_IPI_TO_HART(hart),
+ (void __iomem *)PENDING_REG(gd->arch.plic, gd->arch.boot_hart));
+
+ return 0;
+}
+
+int riscv_clear_ipi(int hart)
+{
+ u32 source_id;
+
+ PLIC_BASE_GET();
+
+ source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
+ writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
+
+ return 0;
+}
+
+static const struct udevice_id andes_plic_ids[] = {
+ { .compatible = "riscv,plic1", .data = RISCV_SYSCON_PLIC },
+ { }
+};
+
+U_BOOT_DRIVER(andes_plic) = {
+ .name = "andes_plic",
+ .id = UCLASS_SYSCON,
+ .of_match = andes_plic_ids,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/arch/riscv/lib/andes_plmt.c b/arch/riscv/lib/andes_plmt.c
new file mode 100644
index 00000000000..84f46075003
--- /dev/null
+++ b/arch/riscv/lib/andes_plmt.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019, Rick Chen <rick@andestech.com>
+ *
+ * U-Boot syscon driver for Andes's Platform Level Machine Timer (PLMT).
+ * The PLMT block holds memory-mapped mtime register
+ * associated with timer tick.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/syscon.h>
+
+/* mtime register */
+#define MTIME_REG(base) ((ulong)(base))
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define PLMT_BASE_GET(void) \
+ do { \
+ long *ret; \
+ \
+ if (!gd->arch.plmt) { \
+ ret = syscon_get_first_range(RISCV_SYSCON_PLMT); \
+ if (IS_ERR(ret)) \
+ return PTR_ERR(ret); \
+ gd->arch.plmt = ret; \
+ } \
+ } while (0)
+
+int riscv_get_time(u64 *time)
+{
+ PLMT_BASE_GET();
+
+ *time = readq((void __iomem *)MTIME_REG(gd->arch.plmt));
+
+ return 0;
+}
+
+static const struct udevice_id andes_plmt_ids[] = {
+ { .compatible = "riscv,plmt0", .data = RISCV_SYSCON_PLMT },
+ { }
+};
+
+U_BOOT_DRIVER(andes_plmt) = {
+ .name = "andes_plmt",
+ .id = UCLASS_SYSCON,
+ .of_match = andes_plmt_ids,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/arch/riscv/lib/asm-offsets.c b/arch/riscv/lib/asm-offsets.c
index e0b71f56919..f998402bd1b 100644
--- a/arch/riscv/lib/asm-offsets.c
+++ b/arch/riscv/lib/asm-offsets.c
@@ -14,6 +14,7 @@
int main(void)
{
DEFINE(GD_BOOT_HART, offsetof(gd_t, arch.boot_hart));
+ DEFINE(GD_AVAILABLE_HARTS, offsetof(gd_t, arch.available_harts));
return 0;
}
diff --git a/arch/riscv/lib/bootm.c b/arch/riscv/lib/bootm.c
index f36b8702ef3..efbd3e23e7a 100644
--- a/arch/riscv/lib/bootm.c
+++ b/arch/riscv/lib/bootm.c
@@ -13,6 +13,7 @@
#include <image.h>
#include <asm/byteorder.h>
#include <asm/csr.h>
+#include <asm/smp.h>
#include <dm/device.h>
#include <dm/root.h>
#include <u-boot/zlib.h>
@@ -81,6 +82,9 @@ static void boot_jump_linux(bootm_headers_t *images, int flag)
{
void (*kernel)(ulong hart, void *dtb);
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
+#ifdef CONFIG_SMP
+ int ret;
+#endif
kernel = (void (*)(ulong, void *))images->ep;
@@ -92,8 +96,15 @@ static void boot_jump_linux(bootm_headers_t *images, int flag)
announce_and_cleanup(fake);
if (!fake) {
- if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
+ if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
+#ifdef CONFIG_SMP
+ ret = smp_call_function(images->ep,
+ (ulong)images->ft_addr, 0);
+ if (ret)
+ hang();
+#endif
kernel(gd->arch.boot_hart, images->ft_addr);
+ }
}
}
diff --git a/arch/riscv/lib/sbi_ipi.c b/arch/riscv/lib/sbi_ipi.c
new file mode 100644
index 00000000000..170346da68c
--- /dev/null
+++ b/arch/riscv/lib/sbi_ipi.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Fraunhofer AISEC,
+ * Lukas Auer <lukas.auer@aisec.fraunhofer.de>
+ */
+
+#include <common.h>
+#include <asm/sbi.h>
+
+int riscv_send_ipi(int hart)
+{
+ ulong mask;
+
+ mask = 1UL << hart;
+ sbi_send_ipi(&mask);
+
+ return 0;
+}
+
+int riscv_clear_ipi(int hart)
+{
+ sbi_clear_ipi();
+
+ return 0;
+}
diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c
new file mode 100644
index 00000000000..caa292ccd20
--- /dev/null
+++ b/arch/riscv/lib/smp.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Fraunhofer AISEC,
+ * Lukas Auer <lukas.auer@aisec.fraunhofer.de>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/barrier.h>
+#include <asm/smp.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * riscv_send_ipi() - Send inter-processor interrupt (IPI)
+ *
+ * Platform code must provide this function.
+ *
+ * @hart: Hart ID of receiving hart
+ * @return 0 if OK, -ve on error
+ */
+extern int riscv_send_ipi(int hart);
+
+/**
+ * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
+ *
+ * Platform code must provide this function.
+ *
+ * @hart: Hart ID of hart to be cleared
+ * @return 0 if OK, -ve on error
+ */
+extern int riscv_clear_ipi(int hart);
+
+static int send_ipi_many(struct ipi_data *ipi)
+{
+ ofnode node, cpus;
+ u32 reg;
+ int ret;
+
+ cpus = ofnode_path("/cpus");
+ if (!ofnode_valid(cpus)) {
+ pr_err("Can't find cpus node!\n");
+ return -EINVAL;
+ }
+
+ ofnode_for_each_subnode(node, cpus) {
+ /* skip if hart is marked as not available in the device tree */
+ if (!ofnode_is_available(node))
+ continue;
+
+ /* read hart ID of CPU */
+ ret = ofnode_read_u32(node, "reg", &reg);
+ if (ret)
+ continue;
+
+ /* skip if it is the hart we are running on */
+ if (reg == gd->arch.boot_hart)
+ continue;
+
+ if (reg >= CONFIG_NR_CPUS) {
+ pr_err("Hart ID %d is out of range, increase CONFIG_NR_CPUS\n",
+ reg);
+ continue;
+ }
+
+ /* skip if hart is not available */
+ if (!(gd->arch.available_harts & (1 << reg)))
+ continue;
+
+ gd->arch.ipi[reg].addr = ipi->addr;
+ gd->arch.ipi[reg].arg0 = ipi->arg0;
+ gd->arch.ipi[reg].arg1 = ipi->arg1;
+
+ ret = riscv_send_ipi(reg);
+ if (ret) {
+ pr_err("Cannot send IPI to hart %d\n", reg);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void handle_ipi(ulong hart)
+{
+ int ret;
+ void (*smp_function)(ulong hart, ulong arg0, ulong arg1);
+
+ if (hart >= CONFIG_NR_CPUS)
+ return;
+
+ ret = riscv_clear_ipi(hart);
+ if (ret) {
+ pr_err("Cannot clear IPI of hart %ld\n", hart);
+ return;
+ }
+
+ __smp_mb();
+
+ smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
+ invalidate_icache_all();
+
+ smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
+}
+
+int smp_call_function(ulong addr, ulong arg0, ulong arg1)
+{
+ int ret = 0;
+ struct ipi_data ipi;
+
+ ipi.addr = addr;
+ ipi.arg0 = arg0;
+ ipi.arg1 = arg1;
+
+ ret = send_ipi_many(&ipi);
+
+ return ret;
+}
diff --git a/board/AndesTech/ax25-ae350/Kconfig b/board/AndesTech/ax25-ae350/Kconfig
index 44cb302f708..5e682b679d8 100644
--- a/board/AndesTech/ax25-ae350/Kconfig
+++ b/board/AndesTech/ax25-ae350/Kconfig
@@ -24,5 +24,6 @@ config ENV_OFFSET
config BOARD_SPECIFIC_OPTIONS # dummy
def_bool y
select RISCV_NDS
+ imply SMP
endif
diff --git a/board/emulation/qemu-riscv/Kconfig b/board/emulation/qemu-riscv/Kconfig
index 88d07d568e7..cf057e7de6a 100644
--- a/board/emulation/qemu-riscv/Kconfig
+++ b/board/emulation/qemu-riscv/Kconfig
@@ -34,5 +34,6 @@ config BOARD_SPECIFIC_OPTIONS # dummy
imply BOARD_LATE_INIT
imply OF_BOARD_SETUP
imply SIFIVE_SERIAL
+ imply SMP
endif
diff --git a/board/sifive/fu540/Kconfig b/board/sifive/fu540/Kconfig
index 6be3d881446..f46437901d1 100644
--- a/board/sifive/fu540/Kconfig
+++ b/board/sifive/fu540/Kconfig
@@ -38,5 +38,6 @@ config BOARD_SPECIFIC_OPTIONS # dummy
imply PHY_LIB
imply PHY_MSCC
imply SIFIVE_SERIAL
+ imply SMP
endif
diff --git a/configs/ae350_rv32_defconfig b/configs/ae350_rv32_defconfig
index 6e0be88d7c9..f02945599f5 100644
--- a/configs/ae350_rv32_defconfig
+++ b/configs/ae350_rv32_defconfig
@@ -34,4 +34,3 @@ CONFIG_BAUDRATE=38400
CONFIG_SYS_NS16550=y
CONFIG_SPI=y
CONFIG_ATCSPI200_SPI=y
-CONFIG_ATCPIT100_TIMER=y
diff --git a/configs/ae350_rv64_defconfig b/configs/ae350_rv64_defconfig
index b472a76d174..98635a2ff90 100644
--- a/configs/ae350_rv64_defconfig
+++ b/configs/ae350_rv64_defconfig
@@ -35,4 +35,3 @@ CONFIG_BAUDRATE=38400
CONFIG_SYS_NS16550=y
CONFIG_SPI=y
CONFIG_ATCSPI200_SPI=y
-CONFIG_ATCPIT100_TIMER=y
diff --git a/include/efi_api.h b/include/efi_api.h
index 8647bfa6629..5b0a1006354 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -333,6 +333,10 @@ struct efi_system_table {
EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, \
0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+#define LOADED_IMAGE_DEVICE_PATH_GUID \
+ EFI_GUID(0xbc62157e, 0x3e33, 0x4fec, \
+ 0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf)
+
#define EFI_LOADED_IMAGE_PROTOCOL_REVISION 0x1000
struct efi_loaded_image {
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 512880ab8fb..00b81c6010f 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -135,6 +135,7 @@ extern const efi_guid_t efi_guid_event_group_reset_system;
/* GUID of the device tree table */
extern const efi_guid_t efi_guid_fdt;
extern const efi_guid_t efi_guid_loaded_image;
+extern const efi_guid_t efi_guid_loaded_image_device_path;
extern const efi_guid_t efi_guid_device_path_to_text_protocol;
extern const efi_guid_t efi_simple_file_system_protocol_guid;
extern const efi_guid_t efi_file_info_guid;
@@ -203,15 +204,11 @@ struct efi_object {
* struct efi_loaded_image_obj - handle of a loaded image
*
* @header: EFI object header
- * @reloc_base: base address for the relocated image
- * @reloc_size: size of the relocated image
* @exit_jmp: long jump buffer for returning form started image
* @entry: entry address of the relocated image
*/
struct efi_loaded_image_obj {
struct efi_object header;
- void *reloc_base;
- aligned_u64 reloc_size;
efi_status_t exit_status;
struct jmp_buf_data exit_jmp;
EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
@@ -320,10 +317,19 @@ efi_status_t efi_create_handle(efi_handle_t *handle);
void efi_delete_handle(efi_handle_t obj);
/* Call this to validate a handle and find the EFI object for it */
struct efi_object *efi_search_obj(const efi_handle_t handle);
+/* Load image */
+efi_status_t EFIAPI efi_load_image(bool boot_policy,
+ efi_handle_t parent_image,
+ struct efi_device_path *file_path,
+ void *source_buffer,
+ efi_uintn_t source_size,
+ efi_handle_t *image_handle);
/* Start image */
efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
efi_uintn_t *exit_data_size,
u16 **exit_data);
+/* Unload image */
+efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle);
/* Find a protocol on a handle */
efi_status_t efi_search_protocol(const efi_handle_t handle,
const efi_guid_t *protocol_guid,
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 417016102b4..4fccadc5483 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -141,6 +141,7 @@ static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
efi_deserialize_load_option(&lo, load_option);
if (lo.attributes & LOAD_OPTION_ACTIVE) {
+ u32 attributes;
efi_status_t ret;
debug("%s: trying to load \"%ls\" from %pD\n",
@@ -151,6 +152,16 @@ static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
if (ret != EFI_SUCCESS)
goto error;
+ attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+ size = sizeof(n);
+ ret = EFI_CALL(efi_set_variable(
+ L"BootCurrent",
+ (efi_guid_t *)&efi_global_variable_guid,
+ attributes, size, &n));
+ if (ret != EFI_SUCCESS)
+ goto error;
+
printf("Booting: %ls\n", lo.label);
efi_dp_split_file_path(lo.file_path, device_path, file_path);
}
@@ -162,21 +173,53 @@ error:
}
/*
- * Attempt to load, in the order specified by BootOrder EFI variable, the
- * available load-options, finding and returning the first one that can
- * be loaded successfully.
+ * Attempt to load from BootNext or in the order specified by BootOrder
+ * EFI variable, the available load-options, finding and returning
+ * the first one that can be loaded successfully.
*/
void *efi_bootmgr_load(struct efi_device_path **device_path,
struct efi_device_path **file_path)
{
- uint16_t *bootorder;
+ u16 bootnext, *bootorder;
efi_uintn_t size;
void *image = NULL;
int i, num;
+ efi_status_t ret;
bs = systab.boottime;
rs = systab.runtime;
+ /* BootNext */
+ bootnext = 0;
+ size = sizeof(bootnext);
+ ret = EFI_CALL(efi_get_variable(L"BootNext",
+ (efi_guid_t *)&efi_global_variable_guid,
+ NULL, &size, &bootnext));
+ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+ /* BootNext does exist here */
+ if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
+ printf("BootNext must be 16-bit integer\n");
+
+ /* delete BootNext */
+ ret = EFI_CALL(efi_set_variable(
+ L"BootNext",
+ (efi_guid_t *)&efi_global_variable_guid,
+ 0, 0, &bootnext));
+
+ /* load BootNext */
+ if (ret == EFI_SUCCESS) {
+ if (size == sizeof(u16)) {
+ image = try_load_entry(bootnext, device_path,
+ file_path);
+ if (image)
+ return image;
+ }
+ } else {
+ printf("Deleting BootNext failed\n");
+ }
+ }
+
+ /* BootOrder */
bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
if (!bootorder) {
printf("BootOrder not defined\n");
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 4fc550d9f37..b215bd7723d 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -26,6 +26,9 @@ LIST_HEAD(efi_obj_list);
/* List of all events */
LIST_HEAD(efi_events);
+/* Handle of the currently executing image */
+static efi_handle_t current_image;
+
/*
* If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
* we need to do trickery with caches. Since we don't want to break the EFI
@@ -1519,6 +1522,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
efi_status_t ret;
struct efi_loaded_image *info = NULL;
struct efi_loaded_image_obj *obj = NULL;
+ struct efi_device_path *dp;
/* In case of EFI_OUT_OF_RESOURCES avoid illegal free by caller. */
*handle_ptr = NULL;
@@ -1542,15 +1546,19 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
if (device_path) {
info->device_handle = efi_dp_find_obj(device_path, NULL);
- /*
- * When asking for the device path interface, return
- * bootefi_device_path
- */
- ret = efi_add_protocol(&obj->header,
- &efi_guid_device_path, device_path);
- if (ret != EFI_SUCCESS)
+
+ dp = efi_dp_append(device_path, file_path);
+ if (!dp) {
+ ret = EFI_OUT_OF_RESOURCES;
goto failure;
+ }
+ } else {
+ dp = NULL;
}
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_loaded_image_device_path, dp);
+ if (ret != EFI_SUCCESS)
+ goto failure;
/*
* When asking for the loaded_image interface, just
@@ -1679,18 +1687,19 @@ error:
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_load_image(bool boot_policy,
- efi_handle_t parent_image,
- struct efi_device_path *file_path,
- void *source_buffer,
- efi_uintn_t source_size,
- efi_handle_t *image_handle)
+efi_status_t EFIAPI efi_load_image(bool boot_policy,
+ efi_handle_t parent_image,
+ struct efi_device_path *file_path,
+ void *source_buffer,
+ efi_uintn_t source_size,
+ efi_handle_t *image_handle)
{
struct efi_device_path *dp, *fp;
struct efi_loaded_image *info = NULL;
struct efi_loaded_image_obj **image_obj =
(struct efi_loaded_image_obj **)image_handle;
efi_status_t ret;
+ void *dest_buffer;
EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
file_path, source_buffer, source_size, image_handle);
@@ -1706,7 +1715,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
}
if (!source_buffer) {
- ret = efi_load_image_from_path(file_path, &source_buffer,
+ ret = efi_load_image_from_path(file_path, &dest_buffer,
&source_size);
if (ret != EFI_SUCCESS)
goto error;
@@ -1719,155 +1728,31 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
/* In this case, file_path is the "device" path, i.e.
* something like a HARDWARE_DEVICE:MEMORY_MAPPED
*/
- u64 addr;
- void *dest_buffer;
-
- ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
- EFI_RUNTIME_SERVICES_CODE,
- efi_size_in_pages(source_size), &addr);
- if (ret != EFI_SUCCESS)
- goto error;
- dest_buffer = (void *)(uintptr_t)addr;
- memcpy(dest_buffer, source_buffer, source_size);
- source_buffer = dest_buffer;
-
+ dest_buffer = source_buffer;
dp = file_path;
fp = NULL;
}
ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
- if (ret != EFI_SUCCESS)
- goto error_invalid_image;
- ret = efi_load_pe(*image_obj, source_buffer, info);
- if (ret != EFI_SUCCESS)
- goto error_invalid_image;
- /* Update the type of the allocated memory */
- efi_add_memory_map((uintptr_t)source_buffer,
- efi_size_in_pages(source_size),
- info->image_code_type, false);
- info->system_table = &systab;
- info->parent_handle = parent_image;
- return EFI_EXIT(EFI_SUCCESS);
-error_invalid_image:
- /* The image is invalid. Release all associated resources. */
- efi_free_pages((uintptr_t)source_buffer,
- efi_size_in_pages(source_size));
- efi_delete_handle(*image_handle);
- *image_handle = NULL;
- free(info);
+ if (ret == EFI_SUCCESS)
+ ret = efi_load_pe(*image_obj, dest_buffer, info);
+ if (!source_buffer)
+ /* Release buffer to which file was loaded */
+ efi_free_pages((uintptr_t)dest_buffer,
+ efi_size_in_pages(source_size));
+ if (ret == EFI_SUCCESS) {
+ info->system_table = &systab;
+ info->parent_handle = parent_image;
+ } else {
+ /* The image is invalid. Release all associated resources. */
+ efi_delete_handle(*image_handle);
+ *image_handle = NULL;
+ free(info);
+ }
error:
return EFI_EXIT(ret);
}
/**
- * efi_start_image() - call the entry point of an image
- * @image_handle: handle of the image
- * @exit_data_size: size of the buffer
- * @exit_data: buffer to receive the exit data of the called image
- *
- * This function implements the StartImage service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * Return: status code
- */
-efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
- efi_uintn_t *exit_data_size,
- u16 **exit_data)
-{
- struct efi_loaded_image_obj *image_obj =
- (struct efi_loaded_image_obj *)image_handle;
- efi_status_t ret;
-
- EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
-
- efi_is_direct_boot = false;
-
- /* call the image! */
- if (setjmp(&image_obj->exit_jmp)) {
- /*
- * We called the entry point of the child image with EFI_CALL
- * in the lines below. The child image called the Exit() boot
- * service efi_exit() which executed the long jump that brought
- * us to the current line. This implies that the second half
- * of the EFI_CALL macro has not been executed.
- */
-#ifdef CONFIG_ARM
- /*
- * efi_exit() called efi_restore_gd(). We have to undo this
- * otherwise __efi_entry_check() will put the wrong value into
- * app_gd.
- */
- gd = app_gd;
-#endif
- /*
- * To get ready to call EFI_EXIT below we have to execute the
- * missed out steps of EFI_CALL.
- */
- assert(__efi_entry_check());
- debug("%sEFI: %lu returned by started image\n",
- __efi_nesting_dec(),
- (unsigned long)((uintptr_t)image_obj->exit_status &
- ~EFI_ERROR_MASK));
- return EFI_EXIT(image_obj->exit_status);
- }
-
- ret = EFI_CALL(image_obj->entry(image_handle, &systab));
-
- /*
- * Usually UEFI applications call Exit() instead of returning.
- * But because the world doesn't consist of ponies and unicorns,
- * we're happy to emulate that behavior on behalf of a payload
- * that forgot.
- */
- return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
-}
-
-/**
- * efi_exit() - leave an EFI application or driver
- * @image_handle: handle of the application or driver that is exiting
- * @exit_status: status code
- * @exit_data_size: size of the buffer in bytes
- * @exit_data: buffer with data describing an error
- *
- * This function implements the Exit service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * Return: status code
- */
-static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
- efi_status_t exit_status,
- efi_uintn_t exit_data_size,
- u16 *exit_data)
-{
- /*
- * TODO: We should call the unload procedure of the loaded
- * image protocol.
- */
- struct efi_loaded_image_obj *image_obj =
- (struct efi_loaded_image_obj *)image_handle;
-
- EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
- exit_data_size, exit_data);
-
- /* Make sure entry/exit counts for EFI world cross-overs match */
- EFI_EXIT(exit_status);
-
- /*
- * But longjmp out with the U-Boot gd, not the application's, as
- * the other end is a setjmp call inside EFI context.
- */
- efi_restore_gd();
-
- image_obj->exit_status = exit_status;
- longjmp(&image_obj->exit_jmp, 1);
-
- panic("EFI application exited");
-}
-
-/**
* efi_unload_image() - unload an EFI image
* @image_handle: handle of the image to be unloaded
*
@@ -1878,7 +1763,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
+efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
{
struct efi_object *efiobj;
@@ -2735,6 +2620,139 @@ out:
}
/**
+ * efi_start_image() - call the entry point of an image
+ * @image_handle: handle of the image
+ * @exit_data_size: size of the buffer
+ * @exit_data: buffer to receive the exit data of the called image
+ *
+ * This function implements the StartImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+ efi_uintn_t *exit_data_size,
+ u16 **exit_data)
+{
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+ efi_status_t ret;
+ void *info;
+ efi_handle_t parent_image = current_image;
+
+ EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+
+ /* Check parameters */
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
+ &info, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ efi_is_direct_boot = false;
+
+ /* call the image! */
+ if (setjmp(&image_obj->exit_jmp)) {
+ /*
+ * We called the entry point of the child image with EFI_CALL
+ * in the lines below. The child image called the Exit() boot
+ * service efi_exit() which executed the long jump that brought
+ * us to the current line. This implies that the second half
+ * of the EFI_CALL macro has not been executed.
+ */
+#ifdef CONFIG_ARM
+ /*
+ * efi_exit() called efi_restore_gd(). We have to undo this
+ * otherwise __efi_entry_check() will put the wrong value into
+ * app_gd.
+ */
+ gd = app_gd;
+#endif
+ /*
+ * To get ready to call EFI_EXIT below we have to execute the
+ * missed out steps of EFI_CALL.
+ */
+ assert(__efi_entry_check());
+ debug("%sEFI: %lu returned by started image\n",
+ __efi_nesting_dec(),
+ (unsigned long)((uintptr_t)image_obj->exit_status &
+ ~EFI_ERROR_MASK));
+ current_image = parent_image;
+ return EFI_EXIT(image_obj->exit_status);
+ }
+
+ current_image = image_handle;
+ ret = EFI_CALL(image_obj->entry(image_handle, &systab));
+
+ /*
+ * Usually UEFI applications call Exit() instead of returning.
+ * But because the world doesn't consist of ponies and unicorns,
+ * we're happy to emulate that behavior on behalf of a payload
+ * that forgot.
+ */
+ return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
+}
+
+/**
+ * efi_exit() - leave an EFI application or driver
+ * @image_handle: handle of the application or driver that is exiting
+ * @exit_status: status code
+ * @exit_data_size: size of the buffer in bytes
+ * @exit_data: buffer with data describing an error
+ *
+ * This function implements the Exit service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
+ efi_status_t exit_status,
+ efi_uintn_t exit_data_size,
+ u16 *exit_data)
+{
+ /*
+ * TODO: We should call the unload procedure of the loaded
+ * image protocol.
+ */
+ efi_status_t ret;
+ void *info;
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+
+ EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
+ exit_data_size, exit_data);
+
+ /* Check parameters */
+ if (image_handle != current_image)
+ goto out;
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
+ &info, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Make sure entry/exit counts for EFI world cross-overs match */
+ EFI_EXIT(exit_status);
+
+ /*
+ * But longjmp out with the U-Boot gd, not the application's, as
+ * the other end is a setjmp call inside EFI context.
+ */
+ efi_restore_gd();
+
+ image_obj->exit_status = exit_status;
+ longjmp(&image_obj->exit_jmp, 1);
+
+ panic("EFI application exited");
+out:
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+/**
* efi_handle_protocol() - get interface of a protocol on a handle
* @handle: handle on which the protocol shall be opened
* @protocol: GUID of the protocol
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index 8e0965bfc89..051fc1d339f 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -797,9 +797,26 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke_ex(
ret = EFI_NOT_READY;
goto out;
}
+ /*
+ * CTRL+A - CTRL+Z have to be signaled as a - z.
+ * SHIFT+CTRL+A - SHIFT+CTRL+Z have to be signaled as A - Z.
+ */
+ switch (next_key.key.unicode_char) {
+ case 0x01 ... 0x07:
+ case 0x0b ... 0x0c:
+ case 0x0e ... 0x1a:
+ if (!(next_key.key_state.key_toggle_state &
+ EFI_CAPS_LOCK_ACTIVE) ^
+ !(next_key.key_state.key_shift_state &
+ (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)))
+ next_key.key.unicode_char += 0x40;
+ else
+ next_key.key.unicode_char += 0x60;
+ }
*key_data = next_key;
key_available = false;
efi_con_in.wait_for_key->is_signaled = false;
+
out:
return EFI_EXIT(ret);
}
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index 0483403be0d..182d735e6bc 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -135,6 +135,25 @@ static int sanitize_path(char *path)
}
/**
+ * efi_create_file() - create file or directory
+ *
+ * @fh: file handle
+ * @attributes: attributes for newly created file
+ * Returns: 0 for success
+ */
+static int efi_create_file(struct file_handle *fh, u64 attributes)
+{
+ loff_t actwrite;
+ void *buffer = &actwrite;
+
+ if (attributes & EFI_FILE_DIRECTORY)
+ return fs_mkdir(fh->path);
+ else
+ return fs_write(fh->path, map_to_sysmem(buffer), 0, 0,
+ &actwrite);
+}
+
+/**
* file_open() - open a file handle
*
* @fs: file system
@@ -176,6 +195,7 @@ static struct efi_file_handle *file_open(struct file_system *fs,
if (parent) {
char *p = fh->path;
+ int exists;
if (plen > 0) {
strcpy(p, parent->path);
@@ -192,18 +212,17 @@ static struct efi_file_handle *file_open(struct file_system *fs,
if (set_blk_dev(fh))
goto error;
- if ((mode & EFI_FILE_MODE_CREATE) &&
- (attributes & EFI_FILE_DIRECTORY)) {
- if (fs_mkdir(fh->path))
- goto error;
- } else if (!((mode & EFI_FILE_MODE_CREATE) ||
- fs_exists(fh->path)))
- goto error;
-
+ exists = fs_exists(fh->path);
/* fs_exists() calls fs_close(), so open file system again */
if (set_blk_dev(fh))
goto error;
+ if (!exists) {
+ if (!(mode & EFI_FILE_MODE_CREATE) ||
+ efi_create_file(fh, attributes))
+ goto error;
+ }
+
/* figure out if file is a directory: */
fh->isdir = is_dir(fh);
} else {
@@ -257,10 +276,12 @@ static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
/* Open file */
*new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
- if (*new_handle)
+ if (*new_handle) {
+ EFI_PRINT("file handle %p\n", *new_handle);
ret = EFI_SUCCESS;
- else
+ } else {
ret = EFI_NOT_FOUND;
+ }
out:
return EFI_EXIT(ret);
}
@@ -616,9 +637,72 @@ static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
efi_uintn_t buffer_size,
void *buffer)
{
- EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer);
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_UNSUPPORTED;
+
+ EFI_ENTRY("%p, %pUl, %zu, %p", file, info_type, buffer_size, buffer);
+
+ if (!guidcmp(info_type, &efi_file_info_guid)) {
+ struct efi_file_info *info = (struct efi_file_info *)buffer;
+ char *filename = basename(fh);
+ char *new_file_name, *pos;
+ loff_t file_size;
- return EFI_EXIT(EFI_UNSUPPORTED);
+ if (buffer_size < sizeof(struct efi_file_info)) {
+ ret = EFI_BAD_BUFFER_SIZE;
+ goto out;
+ }
+ /* We cannot change the directory attribute */
+ if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
+ ret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ /* Check for renaming */
+ new_file_name = malloc(utf16_utf8_strlen(info->file_name));
+ if (!new_file_name) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ pos = new_file_name;
+ utf16_utf8_strcpy(&pos, info->file_name);
+ if (strcmp(new_file_name, filename)) {
+ /* TODO: we do not support renaming */
+ EFI_PRINT("Renaming not supported\n");
+ free(new_file_name);
+ ret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ free(new_file_name);
+ /* Check for truncation */
+ if (set_blk_dev(fh)) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ if (fs_size(fh->path, &file_size)) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ if (file_size != info->file_size) {
+ /* TODO: we do not support truncation */
+ EFI_PRINT("Truncation not supported\n");
+ ret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ /*
+ * We do not care for the other attributes
+ * TODO: Support read only
+ */
+ ret = EFI_SUCCESS;
+ } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
+ if (buffer_size < sizeof(struct efi_file_system_info)) {
+ ret = EFI_BAD_BUFFER_SIZE;
+ goto out;
+ }
+ } else {
+ ret = EFI_UNSUPPORTED;
+ }
+out:
+ return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index fe66e7b9ffe..93feefd366c 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -14,6 +14,8 @@
const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
+const efi_guid_t efi_guid_loaded_image_device_path
+ = LOADED_IMAGE_DEVICE_PATH_GUID;
const efi_guid_t efi_simple_file_system_protocol_guid =
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
@@ -59,10 +61,10 @@ static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj,
{
printf("UEFI image");
printf(" [0x%p:0x%p]",
- obj->reloc_base, obj->reloc_base + obj->reloc_size - 1);
- if (pc && pc >= obj->reloc_base &&
- pc < obj->reloc_base + obj->reloc_size)
- printf(" pc=0x%zx", pc - obj->reloc_base);
+ image->image_base, image->image_base + image->image_size - 1);
+ if (pc && pc >= image->image_base &&
+ pc < image->image_base + image->image_size)
+ printf(" pc=0x%zx", pc - image->image_base);
if (image->file_path)
printf(" '%pD'", image->file_path);
printf("\n");
@@ -227,7 +229,6 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
unsigned long rel_size;
int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
uint64_t image_base;
- uint64_t image_size;
unsigned long virt_size = 0;
int supported = 0;
@@ -271,7 +272,6 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
image_base = opt->ImageBase;
- image_size = opt->SizeOfImage;
efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
efi_reloc = efi_alloc(virt_size,
loaded_image_info->image_code_type);
@@ -287,7 +287,6 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
} else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
image_base = opt->ImageBase;
- image_size = opt->SizeOfImage;
efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
efi_reloc = efi_alloc(virt_size,
loaded_image_info->image_code_type);
@@ -306,6 +305,11 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
return EFI_LOAD_ERROR;
}
+ /* Copy PE headers */
+ memcpy(efi_reloc, efi, sizeof(*dos) + sizeof(*nt)
+ + nt->FileHeader.SizeOfOptionalHeader
+ + num_sections * sizeof(IMAGE_SECTION_HEADER));
+
/* Load sections into RAM */
for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = &sections[i];
@@ -330,10 +334,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
invalidate_icache_all();
/* Populate the loaded image interface bits */
- loaded_image_info->image_base = efi;
- loaded_image_info->image_size = image_size;
- handle->reloc_base = efi_reloc;
- handle->reloc_size = virt_size;
+ loaded_image_info->image_base = efi_reloc;
+ loaded_image_info->image_size = virt_size;
return EFI_SUCCESS;
}
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index 55622d2fb40..dbe29b89603 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -15,6 +15,9 @@
DECLARE_GLOBAL_DATA_PTR;
+/* Magic number identifying memory allocated from pool */
+#define EFI_ALLOC_POOL_MAGIC 0x1fe67ddf6491caa2
+
efi_uintn_t efi_memory_map_key;
struct efi_mem_list {
@@ -33,7 +36,12 @@ LIST_HEAD(efi_mem);
void *efi_bounce_buffer;
#endif
-/*
+/**
+ * efi_pool_allocation - memory block allocated from pool
+ *
+ * @num_pages: number of pages allocated
+ * @checksum: checksum
+ *
* U-Boot services each EFI AllocatePool request as a separate
* (multiple) page allocation. We have to track the number of pages
* to be able to free the correct amount later.
@@ -43,9 +51,26 @@ void *efi_bounce_buffer;
*/
struct efi_pool_allocation {
u64 num_pages;
+ u64 checksum;
char data[] __aligned(ARCH_DMA_MINALIGN);
};
+/**
+ * checksum() - calculate checksum for memory allocated from pool
+ *
+ * @alloc: allocation header
+ * Return: checksum, always non-zero
+ */
+static u64 checksum(struct efi_pool_allocation *alloc)
+{
+ u64 addr = (uintptr_t)alloc;
+ u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
+ EFI_ALLOC_POOL_MAGIC;
+ if (!ret)
+ ++ret;
+ return ret;
+}
+
/*
* Sorts the memory list from highest address to lowest address
*
@@ -204,8 +229,8 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
bool carve_again;
uint64_t carved_pages = 0;
- debug("%s: 0x%llx 0x%llx %d %s\n", __func__,
- start, pages, memory_type, overlap_only_ram ? "yes" : "no");
+ EFI_PRINT("%s: 0x%llx 0x%llx %d %s\n", __func__,
+ start, pages, memory_type, overlap_only_ram ? "yes" : "no");
if (memory_type >= EFI_MAX_MEMORY_TYPE)
return EFI_INVALID_PARAMETER;
@@ -409,17 +434,24 @@ void *efi_alloc(uint64_t len, int memory_type)
return NULL;
}
-/*
- * Free memory pages.
+/**
+ * efi_free_pages() - free memory pages
*
- * @memory start of the memory area to be freed
- * @pages number of pages to be freed
- * @return status code
+ * @memory: start of the memory area to be freed
+ * @pages: number of pages to be freed
+ * Return: status code
*/
efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
{
uint64_t r = 0;
+ /* Sanity check */
+ if (!memory || (memory & EFI_PAGE_MASK)) {
+ printf("%s: illegal free 0x%llx, 0x%zx\n", __func__,
+ memory, pages);
+ return EFI_INVALID_PARAMETER;
+ }
+
r = efi_add_memory_map(memory, pages, EFI_CONVENTIONAL_MEMORY, false);
/* Merging of adjacent free regions is missing */
@@ -429,13 +461,13 @@ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
return EFI_NOT_FOUND;
}
-/*
- * Allocate memory from pool.
+/**
+ * efi_allocate_pool - allocate memory from pool
*
- * @pool_type type of the pool from which memory is to be allocated
- * @size number of bytes to be allocated
- * @buffer allocated memory
- * @return status code
+ * @pool_type: type of the pool from which memory is to be allocated
+ * @size: number of bytes to be allocated
+ * @buffer: allocated memory
+ * Return: status code
*/
efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer)
{
@@ -458,17 +490,18 @@ efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer)
if (r == EFI_SUCCESS) {
alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
alloc->num_pages = num_pages;
+ alloc->checksum = checksum(alloc);
*buffer = alloc->data;
}
return r;
}
-/*
- * Free memory from pool.
+/**
+ * efi_free_pool() - free memory from pool
*
- * @buffer start of memory to be freed
- * @return status code
+ * @buffer: start of memory to be freed
+ * Return: status code
*/
efi_status_t efi_free_pool(void *buffer)
{
@@ -479,8 +512,15 @@ efi_status_t efi_free_pool(void *buffer)
return EFI_INVALID_PARAMETER;
alloc = container_of(buffer, struct efi_pool_allocation, data);
- /* Sanity check, was the supplied address returned by allocate_pool */
- assert(((uintptr_t)alloc & EFI_PAGE_MASK) == 0);
+
+ /* Check that this memory was allocated by efi_allocate_pool() */
+ if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
+ alloc->checksum != checksum(alloc)) {
+ printf("%s: illegal free 0x%p\n", __func__, buffer);
+ return EFI_INVALID_PARAMETER;
+ }
+ /* Avoid double free */
+ alloc->checksum = 0;
r = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index 8266d06c2ef..a908843d87a 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -10,6 +10,9 @@
#define OBJ_LIST_NOT_INITIALIZED 1
+/* Language code for American English according to RFC 4646 */
+#define EN_US L"en-US"
+
static efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED;
/* Initialize and populate EFI object list */
@@ -24,6 +27,30 @@ efi_status_t efi_init_obj_list(void)
*/
efi_save_gd();
+ /*
+ * Variable PlatformLang defines the language that the machine has been
+ * configured for.
+ */
+ ret = EFI_CALL(efi_set_variable(L"PlatformLang",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(EN_US), EN_US));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /*
+ * Variable PlatformLangCodes defines the language codes that the
+ * machine can support.
+ */
+ ret = EFI_CALL(efi_set_variable(L"PlatformLangCodes",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(EN_US), EN_US));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
/* Initialize once only */
if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
return efi_obj_list_initialized;
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index 699f4184d93..37728c3c165 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -180,7 +180,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
if (ret)
return EFI_EXIT(ret);
- debug("%s: get '%s'\n", __func__, native_name);
+ EFI_PRINT("get '%s'\n", native_name);
val = env_get(native_name);
free(native_name);
@@ -211,7 +211,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
if (hex2bin(data, s, len))
return EFI_EXIT(EFI_DEVICE_ERROR);
- debug("%s: got value: \"%s\"\n", __func__, s);
+ EFI_PRINT("got value: \"%s\"\n", s);
} else if ((s = prefix(val, "(utf8)"))) {
unsigned len = strlen(s) + 1;
@@ -226,9 +226,9 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
memcpy(data, s, len);
((char *)data)[len] = '\0';
- debug("%s: got value: \"%s\"\n", __func__, (char *)data);
+ EFI_PRINT("got value: \"%s\"\n", (char *)data);
} else {
- debug("%s: invalid value: '%s'\n", __func__, val);
+ EFI_PRINT("invalid value: '%s'\n", val);
return EFI_EXIT(EFI_DEVICE_ERROR);
}
@@ -485,7 +485,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
s = bin2hex(s, data, data_size);
*s = '\0';
- debug("%s: setting: %s=%s\n", __func__, native_name, val);
+ EFI_PRINT("setting: %s=%s\n", native_name, val);
if (env_set(native_name, val))
ret = EFI_DEVICE_ERROR;
diff --git a/lib/efi_selftest/efi_selftest_miniapp_exit.c b/lib/efi_selftest/efi_selftest_miniapp_exit.c
index 2ffdd65b759..d63b9e3addf 100644
--- a/lib/efi_selftest/efi_selftest_miniapp_exit.c
+++ b/lib/efi_selftest/efi_selftest_miniapp_exit.c
@@ -11,22 +11,70 @@
#include <common.h>
#include <efi_api.h>
-/*
+static efi_guid_t loaded_image_protocol_guid = LOADED_IMAGE_GUID;
+
+/**
+ * check_loaded_image_protocol() - check image_base/image_size
+ *
+ * Try to open the loaded image protocol. Check that this function is located
+ * between image_base and image_base + image_size.
+ *
+ * @image_handle: handle of the loaded image
+ * @systable: system table
+ * @return: status code
+ */
+static efi_status_t EFIAPI check_loaded_image_protocol
+ (efi_handle_t image_handle, struct efi_system_table *systable)
+{
+ struct efi_simple_text_output_protocol *cout = systable->con_out;
+ struct efi_boot_services *boottime = systable->boottime;
+ struct efi_loaded_image *loaded_image_protocol;
+ efi_status_t ret;
+
+ /*
+ * Open the loaded image protocol.
+ */
+ ret = boottime->open_protocol
+ (image_handle, &loaded_image_protocol_guid,
+ (void **)&loaded_image_protocol, NULL,
+ NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS) {
+ cout->output_string(cout,
+ L"Could not open loaded image protocol");
+ return ret;
+ }
+ if ((void *)check_loaded_image_protocol <
+ loaded_image_protocol->image_base ||
+ (void *)check_loaded_image_protocol >=
+ loaded_image_protocol->image_base +
+ loaded_image_protocol->image_size) {
+ cout->output_string(cout,
+ L"Incorrect image_base or image_size\n");
+ return EFI_NOT_FOUND;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
* Entry point of the EFI application.
*
- * @handle handle of the loaded image
- * @systable system table
- * @return status code
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: status code
*/
efi_status_t EFIAPI efi_main(efi_handle_t handle,
struct efi_system_table *systable)
{
struct efi_simple_text_output_protocol *con_out = systable->con_out;
+ efi_status_t ret = EFI_UNSUPPORTED;
con_out->output_string(con_out, L"EFI application calling Exit\n");
+ if (check_loaded_image_protocol(handle, systable) != EFI_SUCCESS)
+ ret = EFI_NOT_FOUND;
+
/* The return value is checked by the calling test */
- systable->boottime->exit(handle, EFI_UNSUPPORTED, 0, NULL);
+ systable->boottime->exit(handle, ret, 0, NULL);
/*
* This statement should not be reached.
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py
index 36b35ee536b..bc226a8e637 100644
--- a/test/py/tests/test_efi_selftest.py
+++ b/test/py/tests/test_efi_selftest.py
@@ -141,7 +141,7 @@ def test_efi_selftest_text_input_ex(u_boot_console):
u_boot_console.run_command(cmd=chr(4), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
m = u_boot_console.p.expect(
- ['Unicode char 4 \(unknown\), scan code 0 \(CTRL\+Null\)'])
+ ['Unicode char 100 \\(\'d\'\\), scan code 0 \\(CTRL\\+Null\\)'])
if m != 0:
raise Exception('EOT failed in \'text input\' test')
u_boot_console.drain_console()