From: Peng Fan <[email protected]> To boot jailhouse before linux, we need a preloader to initialize GIC, boot up all cores, did basic EL2 initialization, then kick inmate/root cell.
This patchset only supports ARM64 with GICv3 and tested on i.MX8MN DDR4 EVK board. The gicv3 initialization is mostly reused from Linux Kernel. The images are all packed into a FIT image, and U-Boot load the FIT images. When bootm, U-Boot will parse the FIT image and copy payload to address specific in fit image. The fit file in the patch is fit.its, currently the address are hardcoded. Later, a script would be used to automatic generate it. The script to generate the FIT image for U-Boot cp hypervisor/jailhouse.bin loader/ cp configs/arm64/imx8mn.cell loader/ cp configs/arm64/imx8mn-gic-demo.cell loader/ cp inmates/demos/arm64/gic-demo.bin loader/ cp <Linux>/arch/arm64/boot/dts/freescale/imx8mn-ddr4-evk-root.dtb ./loader cp <Linux>/arch/arm64/boot/Image ./loader cd loader mkimage -E -f fit.its loader.itb cd - Currently only root cell linux plus inmate cell gic demo work. root cell will not able manage inmate cell. But I think root cell could manage, if we modify the jailhouse driver to get the cell information. Intercell communication currently not supported, this could added later. The boot command: fatload mmc 1:1 0x60000000 loader.itb bootm 0x60000000 Post this as RFC, hope we could have an good direction and got this feature accepted by Jailhouse community. Signed-off-by: Peng Fan <[email protected]> --- Kbuild | 2 +- hypervisor/arch/arm-common/include/asm/gic.h | 3 + hypervisor/arch/arm-common/include/asm/gic_v3.h | 18 ++ loader/Makefile | 49 ++++ loader/configs.h | 30 +++ loader/fit.its | 85 ++++++ loader/gic-v3.c | 154 +++++++++++ loader/head.S | 327 ++++++++++++++++++++++++ loader/inmate.c | 63 +++++ loader/lib.c | 56 ++++ loader/loader.h | 15 ++ loader/loader.lds.S | 66 +++++ loader/main.c | 194 ++++++++++++++ loader/mmio.h | 21 ++ loader/psci.h | 119 +++++++++ 15 files changed, 1201 insertions(+), 1 deletion(-) create mode 100644 loader/Makefile create mode 100644 loader/configs.h create mode 100644 loader/fit.its create mode 100644 loader/gic-v3.c create mode 100644 loader/head.S create mode 100644 loader/inmate.c create mode 100644 loader/lib.c create mode 100644 loader/loader.h create mode 100644 loader/loader.lds.S create mode 100644 loader/main.c create mode 100644 loader/mmio.h create mode 100644 loader/psci.h diff --git a/Kbuild b/Kbuild index 0b25e26e..1751f13a 100644 --- a/Kbuild +++ b/Kbuild @@ -51,7 +51,7 @@ GEN_PCI_DEFS_PY := $(obj)/pyjailhouse/pci_defs.py $(GEN_PCI_DEFS_PY): $(src)/scripts/gen_pci_defs.sh $(call if_changed,gen_pci_defs) -subdir-y := hypervisor configs inmates tools +subdir-y := hypervisor configs inmates tools loader subdir-ccflags-y := -Werror diff --git a/hypervisor/arch/arm-common/include/asm/gic.h b/hypervisor/arch/arm-common/include/asm/gic.h index e851d375..af9c3b4c 100644 --- a/hypervisor/arch/arm-common/include/asm/gic.h +++ b/hypervisor/arch/arm-common/include/asm/gic.h @@ -33,10 +33,13 @@ #define GICD_SGIR 0x0f00 #define GICD_CPENDSGIR 0x0f10 #define GICD_SPENDSGIR 0x0f20 +#define GIC_DIST_CONFIG 0xc00 #define GICD_IROUTER 0x6000 #define GICD_PIDR2_ARCH(pidr) (((pidr) & 0xf0) >> 4) +#define GICD_INT_ACTLOW_LVLTRIG 0x0 + #define is_sgi(irqn) ((u32)(irqn) < 16) #define is_ppi(irqn) ((irqn) > 15 && (irqn) < 32) #define is_spi(irqn) ((irqn) > 31 && (irqn) < 1020) diff --git a/hypervisor/arch/arm-common/include/asm/gic_v3.h b/hypervisor/arch/arm-common/include/asm/gic_v3.h index 853721d6..c8990d00 100644 --- a/hypervisor/arch/arm-common/include/asm/gic_v3.h +++ b/hypervisor/arch/arm-common/include/asm/gic_v3.h @@ -21,6 +21,23 @@ #define GICDv3_PIDR2 0xffe8 #define GICDv3_PIDR4 0xffd0 +#define GICD_IGROUPRnE 0x1000 +#define GICD_ICENABLERnE 0x1400 +#define GICD_ICACTIVERnE 0x1C00 +#define GICD_IPRIORITYRnE 0x2000 +#define GICD_ICFGRnE 0x3000 +#define GICD_IROUTERnE 0x8000 + +#define GICD_CTLR_RWP (1U << 31) +#define GICD_CTLR_ENABLE_G1A (1U << 1) +#define GICD_CTLR_ENABLE_G1 (1U << 0) + +#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1) +#define GICD_TYPER_NUM_LPIS(typer) ((((typer) >> 11) & 0x1f) + 1) +#define GICD_TYPER_SPIS(typer) ((((typer) & 0x1f) + 1) * 32) +#define GICD_TYPER_ESPIS(typer) \ + (((typer) & GICD_TYPER_ESPI) ? GICD_TYPER_SPIS((typer) >> 27) : 0) + #define GICR_CTLR 0x0000 #define GICR_IIDR 0x0004 #define GICR_TYPER 0x0008 @@ -39,6 +56,7 @@ #define GICR_IPRIORITYR GICD_IPRIORITYR #define GICR_ICFGR GICD_ICFGR +#define GICD_TYPER_ESPI (1U << 8) #define GICR_TYPER_Last (1 << 4) #define GICR_PIDR2_ARCH GICD_PIDR2_ARCH diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 00000000..7ddf8035 --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,49 @@ +-include $(GEN_CONFIG_MK) + +LINUXINCLUDE := -I$(src)/../hypervisor/arch/$(SRCARCH)/include \ + -I$(src)/../hypervisor/include \ + -I$(src)/../include/arch/$(SRCARCH) \ + -I$(src)/../hypervisor/arch/$(SRCARCH)/include \ + -I$(src)/../hypervisor/arch/arm-common/include/ \ + -I$(src)/../include \ + -I$(src) + +KBUILD_AFLAGS := -D__ASSEMBLY__ -fno-PIE +KBUILD_CFLAGS := -g -Os -Wall -Wstrict-prototypes -Wtype-limits \ + -Wmissing-declarations -Wmissing-prototypes \ + -fno-strict-aliasing -fno-pic -fno-common \ + -fno-stack-protector -fno-builtin-ffsl \ + -D__LINUX_COMPILER_TYPES_H -mstrict-align +CORE_OBJECTS = head.o gic-v3.o main.o lib.o inmate.o + +ifneq ($(wildcard $(INC_CONFIG_H)),) +KBUILD_CFLAGS += -include $(INC_CONFIG_H) +endif + +define BUILD_loader_template +always += loader$(1).bin + +$$(obj)/arch/$$(SRCARCH)/lib$(1).a: $$(obj)/arch/$$(SRCARCH) + @true + +loader$(1)-y := $$(CORE_OBJECTS) loader.lds +targets += $$(loader$(1)-y) + +loader$(1)_OBJS = $$(addprefix $$(obj)/,$$(loader$(1)-y)) + +LDFLAGS_loader$(1).o := --whole-archive -T + +targets += loader$(1).o +$$(obj)/loader$(1).o: $$(src)/loader.lds $$(loader$(1)_OBJS) + $$(call if_changed,ld) + +OBJCOPYFLAGS_loader$(1).bin := -O binary -R .eh_frame + +targets += loader$(1).bin +$$(obj)/loader$(1).bin: $$(obj)/loader$(1).o + $$(call if_changed,objcopy) +endef + +ifeq ($(SRCARCH),arm64) +$(eval $(call BUILD_loader_template,)) +endif diff --git a/loader/configs.h b/loader/configs.h new file mode 100644 index 00000000..fce5f366 --- /dev/null +++ b/loader/configs.h @@ -0,0 +1,30 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright 2020 NXP + * + * Authors: + * Peng Fan <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#define LOADER_ADDR 0x40000000 +#define JAILHOUSE_ADDR 0x40200000 + +#define CELL_STRIDE 0x10000 + +#define CELL0_CONF_ADDR 0x40300000 +#define CELL0_CONF_FILE "imx8mn.cell" +#define CELL0_FDT_FILE "imx8mn-ddr4-evk-root.dtb" +#define CELL0_FDT_ADDR 0x43000000 +#define CELL0_INMATE_ADDR 0x40480000 + +#define CELL1_CONF_ADDR 0x40310000 +#define CELL1_CONF_FILE "imx8mn-gic-demo.cell" +#define CELL1_INMATE_ADDR 0xb3c00000 +#define CELL1_INMATE_FILE "gic-demo.bin" + +#define GICD_ADDR 0x38800000 + diff --git a/loader/fit.its b/loader/fit.its new file mode 100644 index 00000000..84d5eb37 --- /dev/null +++ b/loader/fit.its @@ -0,0 +1,85 @@ +/dts-v1/; + +/ { + description = "Configuration to load Images"; + + images { + kernel { + description = "linux"; + os = "Linux"; + data = /incbin/("loader.bin"); + type = "kernel"; + arch = "arm64"; + compression = "none"; + load = <0x40000000>; + entry = <0x40000000>; + }; + + hypervisor { + description = "Jailhouse hypervisor"; + os = "jailhouse"; + data = /incbin/("jailhouse.bin"); + type = "standalone"; + arch = "arm64"; + compression = "none"; + load = <0x40200000>; + }; + + cell@0 { + description = "i.MX8MN cell"; + data = /incbin/("imx8mn.cell"); + arch = "arm64"; + type = "standalone"; + compression = "none"; + load = <0x40300000>; + }; + + inmate@0 { + description = "ROOT CELL image"; + os = "linux"; + arch = "arm64"; + data = /incbin/("Image"); + type = "standalone"; + compression = "none"; + load = <0x40480000>; + }; + + cell@1 { + description = "i.MX8MN GIC-CELL"; + data = /incbin/("imx8mn-gic-demo.cell"); + arch = "arm64"; + type = "standalone"; + compression = "none"; + load = <0x40310000>; + }; + + inmate@1 { + description = "i.MX8MN GIC-DEMO"; + data = /incbin/("gic-demo.bin"); + arch = "arm64"; + type = "standalone"; + compression = "none"; + load = <0xb3c00000>; + }; + + fdt { + description = "fdt"; + data = /incbin/("imx8mn-ddr4-evk-root.dtb"); + type = "flat_dt"; + compression = "none"; + arch = "arm64"; + load = <0x43000000>; + }; + }; + + configurations { + default = "config@1"; + + config@1 { + description = "imx8mp-evk"; + kernel = "kernel"; + fdt = "fdt"; + loadables = "hypervisor", "cell@0", "inmate@0", "cell@1", "inmate@1"; + }; + }; +}; diff --git a/loader/gic-v3.c b/loader/gic-v3.c new file mode 100644 index 00000000..02fd1dcb --- /dev/null +++ b/loader/gic-v3.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2002 ARM Limited, All Rights Reserved. + */ + +#include <jailhouse/types.h> +#include <asm/gic.h> +#include <asm/gic_v3.h> +#include <jailhouse/mmio.h> +#include <loader.h> +#include <mmio.h> +#include <configs.h> + +#define GICD_INT_DEF_PRI 0xa0 +#define GICD_INT_DEF_PRI_X4 ((GICD_INT_DEF_PRI << 24) |\ + (GICD_INT_DEF_PRI << 16) |\ + (GICD_INT_DEF_PRI << 8) |\ + GICD_INT_DEF_PRI) +struct rdists { + unsigned int gicd_typer; + bool has_vlpis; + bool has_direct_lpi; +}; + +struct gic_chip_data { + void *dist_base; + struct rdists rdists; +}; + +static struct gic_chip_data gic_data; + +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define GIC_ID_NR (1U << GICD_TYPER_ID_BITS(gic_data.rdists.gicd_typer)) +#define GIC_LINE_NR min(GICD_TYPER_SPIS(gic_data.rdists.gicd_typer), 1020U) +#define GIC_ESPI_NR GICD_TYPER_ESPIS(gic_data.rdists.gicd_typer) + +static void gic_dist_config(void *base, int gic_irqs, + void (*sync_access)(void)) +{ + unsigned int i; + + /* + * Set all global interrupts to be level triggered, active low. + */ + for (i = 32; i < gic_irqs; i += 16) + writel(GICD_INT_ACTLOW_LVLTRIG, + base + GIC_DIST_CONFIG + i / 4); + + /* + * Set priority on all global interrupts. + */ + for (i = 32; i < gic_irqs; i += 4) + writel(GICD_INT_DEF_PRI_X4, base + GICD_IPRIORITYR + i); + + /* + * Deactivate and disable all SPIs. Leave the PPI and SGIs + * alone as they are in the redistributor registers on GICv3. + */ + for (i = 32; i < gic_irqs; i += 32) { + writel(0xffffffff, base + GICD_ICACTIVER + i / 8); + writel(0xffffffff, base + GICD_ICENABLER + i / 8); + } + + if (sync_access) + sync_access(); +} + +static void gic_do_wait_for_rwp(void *base) +{ + u32 count = 0xffffffff; + + while (readl(base + GICD_CTLR) & GICD_CTLR_RWP) { + count--; + if (!count) { + return; + } + cpu_relax(); + } +} + +/* Wait for completion of a distributor change */ +static void gic_dist_wait_for_rwp(void) +{ + gic_do_wait_for_rwp(gic_data.dist_base); +} + +#define MPIDR_EL1 SYSREG_32(0, c0, c0, 5) +static unsigned long phys_processor_id(void) +{ + unsigned long mpidr; + + arm_read_sysreg(MPIDR_EL1, mpidr); + return mpidr & MPIDR_CPUID_MASK; +} + +void gic_dist_init(void) +{ + unsigned int i; + u64 affinity; + void *base; + u32 typer; + + gic_data.dist_base = (void *)GICD_ADDR; + + base = gic_data.dist_base; + typer = readl(gic_data.dist_base + GICD_TYPER); + gic_data.rdists.gicd_typer = typer; + + /* Disable the distributor */ + writel(0, base + GICD_CTLR); + gic_dist_wait_for_rwp(); + + /* + * Configure SPIs as non-secure Group-1. This will only matter + * if the GIC only has a single security state. This will not + * do the right thing if the kernel is running in secure mode, + * but that's not the intended use case anyway. + */ + for (i = 32; i < GIC_LINE_NR; i += 32) + writel(~0, base + GICD_IGROUPR + i / 8); + + /* Extended SPI range, not handled by the GICv2/GICv3 common code */ + for (i = 0; i < GIC_ESPI_NR; i += 32) { + writel(~0U, base + GICD_ICENABLERnE + i / 8); + writel(~0U, base + GICD_ICACTIVERnE + i / 8); + } + + for (i = 0; i < GIC_ESPI_NR; i += 32) + writel(~0U, base + GICD_IGROUPRnE + i / 8); + + for (i = 0; i < GIC_ESPI_NR; i += 16) + writel(0, base + GICD_ICFGRnE + i / 4); + + for (i = 0; i < GIC_ESPI_NR; i += 4) + writel(GICD_INT_DEF_PRI_X4, base + GICD_IPRIORITYRnE + i); + + /* Now do the common stuff, and wait for the distributor to drain */ + gic_dist_config(base, GIC_LINE_NR, gic_dist_wait_for_rwp); + + /* Enable distributor with ARE, Group1 */ + writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1, + base + GICD_CTLR); + + /* + * Set all global interrupts to the boot CPU only. ARE must be + * enabled. + */ + affinity = phys_processor_id(); + for (i = 32; i < GIC_LINE_NR; i++) + mmio_write64(base + GICD_IROUTER + i * 8, affinity); + + for (i = 0; i < GIC_ESPI_NR; i++) + mmio_write64(base + GICD_IROUTERnE + i * 8, affinity); +} diff --git a/loader/head.S b/loader/head.S new file mode 100644 index 00000000..323551da --- /dev/null +++ b/loader/head.S @@ -0,0 +1,327 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Low-level CPU initialisation + * Based on arch/arm/kernel/head.S + * + * Copyright (C) 1994-2002 Russell King + * Copyright (C) 2003-2012 ARM Ltd. + * Authors: Catalin Marinas <[email protected]> + * Will Deacon <[email protected]> + */ + +/* AArch64 SPSR bits */ +#define PSR_F_BIT 0x00000040 +#define PSR_I_BIT 0x00000080 +#define PSR_A_BIT 0x00000100 +#define PSR_D_BIT 0x00000200 +#define PSR_SSBS_BIT 0x00001000 +#define PSR_PAN_BIT 0x00400000 +#define PSR_UAO_BIT 0x00800000 +#define PSR_V_BIT 0x10000000 +#define PSR_C_BIT 0x20000000 +#define PSR_Z_BIT 0x40000000 +#define PSR_N_BIT 0x80000000 + +#define PSR_MODE_EL1h 0x00000005 +#define CurrentEL_EL2 (2 << 2) +#define CPTR_EL2_TZ (1 << 8) +#define ZCR_ELx_LEN_MASK 0x1ff + +#define __emit_inst(x) .inst (x) + + .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 + .equ .L__reg_num_x\num, \num + .endr + .equ .L__reg_num_xzr, 31 + + .macro mrs_s, rt, sreg + __emit_inst(0xd5200000|(\sreg)|(.L__reg_num_\rt)) + .endm + + .macro msr_s, sreg, rt + __emit_inst(0xd5000000|(\sreg)|(.L__reg_num_\rt)) + .endm + /* + * mov_q - move an immediate constant into a 64-bit register using + * between 2 and 4 movz/movk instructions (depending on the + * magnitude and sign of the operand) + */ + .macro mov_q, reg, val + .if (((\val) >> 31) == 0 || ((\val) >> 31) == 0x1ffffffff) + movz \reg, :abs_g1_s:\val + .else + .if (((\val) >> 47) == 0 || ((\val) >> 47) == 0x1ffff) + movz \reg, :abs_g2_s:\val + .else + movz \reg, :abs_g3:\val + movk \reg, :abs_g2_nc:\val + .endif + movk \reg, :abs_g1_nc:\val + .endif + movk \reg, :abs_g0_nc:\val + .endm + +#define BIT(nr) ((1) << (nr)) +#define SCTLR_EL2_RES1 ((BIT(4)) | (BIT(5)) | (BIT(11)) | (BIT(16)) | \ + (BIT(18)) | (BIT(22)) | (BIT(23)) | (BIT(28)) | \ + (BIT(29))) + +#define SCTLR_EL1_RES1 ((BIT(11)) | (BIT(20)) | (BIT(22)) | (BIT(28)) | \ + (BIT(29))) + +#ifdef CONFIG_CPU_BIG_ENDIAN +#define ENDIAN_SET_EL1 (SCTLR_EL1_E0E | SCTLR_ELx_EE) +#else +#define ENDIAN_SET_EL1 0 +#endif + + +#define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK) + +#define HCR_APK ((1UL) << 40) +#define HCR_API ((1UL) << 41) +#define HCR_RW_SHIFT 31 +#define HCR_RW ((1UL) << HCR_RW_SHIFT) + +#ifdef CONFIG_CPU_BIG_ENDIAN +#define ENDIAN_SET_EL2 (SCTLR_ELx_EE) +#else +#define ENDIAN_SET_EL2 0 +#endif + +/* + * ARMv8 ARM reserves the following encoding for system registers: + * (Ref: ARMv8 ARM, Section: "System instruction class encoding overview", + * C5.2, version:ARM DDI 0487A.f) + * [20-19] : Op0 + * [18-16] : Op1 + * [15-12] : CRn + * [11-8] : CRm + * [7-5] : Op2 + */ +#define Op0_shift 19 +#define Op0_mask 0x3 +#define Op1_shift 16 +#define Op1_mask 0x7 +#define CRn_shift 12 +#define CRn_mask 0xf +#define CRm_shift 8 +#define CRm_mask 0xf +#define Op2_shift 5 +#define Op2_mask 0x7 + +#define sys_reg(op0, op1, crn, crm, op2) \ + (((op0) << Op0_shift) | ((op1) << Op1_shift) | \ + ((crn) << CRn_shift) | ((crm) << CRm_shift) | \ + ((op2) << Op2_shift)) + +#define ID_AA64PFR0_GIC_SHIFT 24 +#define ID_AA64DFR0_PMUVER_SHIFT 8 +#define ID_AA64DFR0_PMSVER_SHIFT 32 +#define SYS_PMBIDR_EL1 sys_reg(3, 0, 9, 10, 7) +#define SYS_PMSCR_EL2 sys_reg(3, 4, 9, 9, 0) +#define SYS_LORC_EL1 sys_reg(3, 0, 10, 4, 3) +#define SYS_ZCR_EL2 sys_reg(3, 4, 1, 2, 0) +#define ID_AA64MMFR1_LOR_SHIFT 16 +#define ID_AA64PFR0_SVE_SHIFT 32 + + .text + .global el2_entry + +el2_entry: + msr SPsel, #1 // We want to use SP_EL{1,2} + mrs x0, CurrentEL + cmp x0, #CurrentEL_EL2 + b.eq 1f + b . + +1: mov_q x0, (SCTLR_EL2_RES1 | ENDIAN_SET_EL2) + msr sctlr_el2, x0 + + /* Set Stack */ + mrs x1, mpidr_el1 + and x1, x1, #0xff + mov x2, #0x200 + mul x1, x1, x2 + mov x0, #0x910000 + add x0, x0, x1 + msr sp_el1, x0 + + mov x2, xzr + + /* Hyp configuration. */ + mov_q x0, HCR_HOST_NVHE_FLAGS + msr hcr_el2, x0 + isb + + /* + * Allow Non-secure EL1 and EL0 to access physical timer and counter. + * This is not necessary for VHE, since the host kernel runs in EL2, + * and EL0 accesses are configured in the later stage of boot process. + * Note that when HCR_EL2.E2H == 1, CNTHCTL_EL2 has the same bit layout + * as CNTKCTL_EL1, and CNTKCTL_EL1 accessing instructions are redefined + * to access CNTHCTL_EL2. This allows the kernel designed to run at EL1 + * to transparently mess with the EL0 bits via CNTKCTL_EL1 access in + * EL2. + */ + cbnz x2, 1f + mrs x0, cnthctl_el2 + orr x0, x0, #3 // Enable EL1 physical timers + msr cnthctl_el2, x0 +1: + msr cntvoff_el2, xzr // Clear virtual offset + +#ifdef CONFIG_ARM_GIC_V3 + /* GICv3 system register access */ + mrs x0, id_aa64pfr0_el1 + ubfx x0, x0, #ID_AA64PFR0_GIC_SHIFT, #4 + cbz x0, 3f + + mrs_s x0, SYS_ICC_SRE_EL2 + orr x0, x0, #ICC_SRE_EL2_SRE // Set ICC_SRE_EL2.SRE==1 + orr x0, x0, #ICC_SRE_EL2_ENABLE // Set ICC_SRE_EL2.Enable==1 + msr_s SYS_ICC_SRE_EL2, x0 + isb // Make sure SRE is now set + mrs_s x0, SYS_ICC_SRE_EL2 // Read SRE back, + tbz x0, #0, 3f // and check that it sticks + msr_s SYS_ICH_HCR_EL2, xzr // Reset ICC_HCR_EL2 to defaults + +3: +#endif + + /* Populate ID registers. */ + mrs x0, midr_el1 + mrs x1, mpidr_el1 + msr vpidr_el2, x0 + msr vmpidr_el2, x1 + +#ifdef CONFIG_COMPAT + msr hstr_el2, xzr // Disable CP15 traps to EL2 +#endif + +#if 0 + /* EL2 debug */ + mrs x1, id_aa64dfr0_el1 + sbfx x0, x1, #ID_AA64DFR0_PMUVER_SHIFT, #4 + cmp x0, #1 + b.lt 4f // Skip if no PMU present + mrs x0, pmcr_el0 // Disable debug access traps + ubfx x0, x0, #11, #5 // to EL2 and allow access to +4: + csel x3, xzr, x0, lt // all PMU counters from EL1 + + /* Statistical profiling */ + ubfx x0, x1, #ID_AA64DFR0_PMSVER_SHIFT, #4 + cbz x0, 7f // Skip if SPE not present + cbnz x2, 6f // VHE? + mrs_s x4, SYS_PMBIDR_EL1 // If SPE available at EL2, + and x4, x4, #(1 << SYS_PMBIDR_EL1_P_SHIFT) + cbnz x4, 5f // then permit sampling of physical + mov x4, #(1 << SYS_PMSCR_EL2_PCT_SHIFT | \ + 1 << SYS_PMSCR_EL2_PA_SHIFT) + msr_s SYS_PMSCR_EL2, x4 // addresses and physical counter +5: + mov x1, #(MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT) + orr x3, x3, x1 // If we don't have VHE, then + b 7f // use EL1&0 translation. +6: // For VHE, use EL2 translation + orr x3, x3, #MDCR_EL2_TPMS // and disable access from EL1 +7: + msr mdcr_el2, x3 // Configure debug traps +#endif + +#if 0 + /* LORegions */ + mrs x1, id_aa64mmfr1_el1 + ubfx x0, x1, #ID_AA64MMFR1_LOR_SHIFT, 4 + cbz x0, 1f + msr_s SYS_LORC_EL1, xzr +#endif +1: + + /* Stage-2 translation */ + msr vttbr_el2, xzr + + cbz x2, install_el2_stub + + isb + ret + +install_el2_stub: + /* + * When VHE is not in use, early init of EL2 and EL1 needs to be + * done here. + * When VHE _is_ in use, EL1 will not be used in the host and + * requires no configuration, and all non-hyp-specific EL2 setup + * will be done via the _EL1 system register aliases in __cpu_setup. + */ + mov_q x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1) + msr sctlr_el1, x0 + + /* Coprocessor traps. */ + mov x0, #0x33ff + msr cptr_el2, x0 // Disable copro. traps to EL2 + + /* SVE register access */ + mrs x1, id_aa64pfr0_el1 + ubfx x1, x1, #ID_AA64PFR0_SVE_SHIFT, #4 + cbz x1, 7f + + bic x0, x0, #CPTR_EL2_TZ // Also disable SVE traps + msr cptr_el2, x0 // Disable copro. traps to EL2 + isb + mov x1, #ZCR_ELx_LEN_MASK // SVE: Enable full vector + msr_s SYS_ZCR_EL2, x1 // length for EL1. + + /* Hypervisor stub */ +7: adr x0, __hyp_stub_vectors + msr vbar_el2, x0 + + /* spsr */ + adr x1, entry + mov x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\ + PSR_MODE_EL1h) + msr spsr_el2, x0 + msr elr_el2, x1 + mrs x0, mpidr_el1 + eret + +.macro ventry label + .align 7 + b \label +.endm + .align 11 + + .global __hyp_stub_vectors +__hyp_stub_vectors: + ventry . + ventry . + ventry . + ventry . + + ventry . + ventry . + ventry . + ventry . + + ventry el1_sync // Synchronous 64-bit EL1 + ventry . + ventry . + ventry . + + ventry . + ventry . + ventry . + ventry . + + + .align 11 + +#define HVC_SET_VECTORS 0 +el1_sync: + cmp x0, #HVC_SET_VECTORS + b.ne 9f + msr vbar_el2, x1 + b 9f +9: mov x0, xzr + eret diff --git a/loader/inmate.c b/loader/inmate.c new file mode 100644 index 00000000..de61ce6b --- /dev/null +++ b/loader/inmate.c @@ -0,0 +1,63 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright 2020 NXP + * + * Authors: + * Peng Fan <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include <jailhouse/types.h> + +#include <configs.h> +#include <loader.h> +#include <psci.h> +#include <jailhouse/cell-config.h> +#include <jailhouse/header.h> +#include <jailhouse/hypercall.h> +#include <jailhouse/string.h> +#include <generated/version.h> + + +#define MAX_CELL 16 + +/* TODO: Handle PCI */ +int inmate_cell(void) +{ + struct jailhouse_cell_desc *config; + int i, err; + + /* Ignore ROOT CELL */ + for (i = 1; i < MAX_CELL; i++) { + config = (struct jailhouse_cell_desc *)(CELL0_CONF_ADDR + i * CELL_STRIDE); + if (memcmp(config->signature, + JAILHOUSE_CELL_DESC_SIGNATURE, + sizeof(config->signature)) != 0) + break; + + if (config->revision != JAILHOUSE_CONFIG_REVISION) + break; + + config->name[JAILHOUSE_CELL_NAME_MAXLEN] = 0; + + config->id = i; + + err = jailhouse_call_arg1(JAILHOUSE_HC_CELL_CREATE, (unsigned long)config); + + if (err) + asm volatile("b .\r\n"); + + err = jailhouse_call_arg1(JAILHOUSE_HC_CELL_SET_LOADABLE, config->id); + if (err) + asm volatile("b .\r\n"); + + err = jailhouse_call_arg1(JAILHOUSE_HC_CELL_START, config->id); + if (err) + asm volatile("b .\r\n"); + } + + return 0; +} diff --git a/loader/lib.c b/loader/lib.c new file mode 100644 index 00000000..096bd317 --- /dev/null +++ b/loader/lib.c @@ -0,0 +1,56 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) Siemens AG, 2013 + * + * Authors: + * Jan Kiszka <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include <loader.h> +#include <jailhouse/string.h> +#include <jailhouse/types.h> + +void *memset(void *s, int c, unsigned long n) +{ + u8 *p = s; + + while (n-- > 0) + *p++ = c; + return s; +} + +int strcmp(const char *s1, const char *s2) +{ + while (*s1 == *s2) { + if (*s1 == '\0') + return 0; + s1++; + s2++; + } + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +void *memcpy(void *dest, const void *src, unsigned long n) +{ + const u8 *s = src; + u8 *d = dest; + + while (n-- > 0) + *d++ = *s++; + return dest; +} + +int memcmp(const void *s1, const void *s2, unsigned long n) +{ + const unsigned char *_s1 = s1, *_s2 = s2; + + while (n-- > 0) + if (*_s1++ != *_s2++) + return _s1[-1] < _s2[-1] ? -1 : 1; + return 0; +} + diff --git a/loader/loader.h b/loader/loader.h new file mode 100644 index 00000000..acd142be --- /dev/null +++ b/loader/loader.h @@ -0,0 +1,15 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) Siemens AG, 2013-2016 + * + * Authors: + * Jan Kiszka <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +int memcmp(const void *s1, const void *s2, unsigned long n); +int inmate_cell(void); +void gic_dist_init(void); diff --git a/loader/loader.lds.S b/loader/loader.lds.S new file mode 100644 index 00000000..58aaa4cb --- /dev/null +++ b/loader/loader.lds.S @@ -0,0 +1,66 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) Siemens AG, 2013-2017 + * + * Authors: + * Jan Kiszka <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include <configs.h> +#include <jailhouse/header.h> + +#include <asm/paging.h> +#include <asm/sections.h> + +SECTIONS +{ + . = LOADER_ADDR; + .header : { *(.header) } + + . = ALIGN(16); + .text : { + __text_start = .; + *(.text) + } + + . = ALIGN(16); + .rodata : { *(.rodata) } + + . = ALIGN(16); + .data : { *(.data) } + + . = ALIGN(8); + .init_array : { + __init_array_start = .; + *(SORT(.init_array.*)) *(.init_array) + __init_array_end = .; + } + + .units : { + __unit_array_start = .; + *(.units); + __unit_array_end = .; + } + + ARCH_SECTIONS + + /* The console section shall only contain the hypervisor console. This + * section and the next section must be aligned to PAGE_SIZE, as we + * will map the console section, and only that section, as a whole page + * to the root cell. */ + + . = ALIGN(PAGE_SIZE); + .console : { *(.console) } + + . = ALIGN(PAGE_SIZE); + .bss : { *(.bss) } + + . = ALIGN(PAGE_SIZE); + __page_pool = .; + + .eh_frame : { *(.eh_frame*) } +} diff --git a/loader/main.c b/loader/main.c new file mode 100644 index 00000000..e2d76b80 --- /dev/null +++ b/loader/main.c @@ -0,0 +1,194 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright 2020 NXP + * + * Authors: + * Peng Fan <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include <configs.h> +#include <loader.h> +#include <psci.h> +#include <jailhouse/string.h> +#include <jailhouse/types.h> +#include <jailhouse/cell-config.h> +#include <jailhouse/header.h> +#include <jailhouse/hypercall.h> +#include <generated/version.h> + +#define PSCI_POWER_STATE_TYPE_STANDBY 0 +#define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 + +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name + +static unsigned long call_smcc64(unsigned long fid, unsigned long a0, + unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long *ret) +{ + register unsigned long x0 asm("x0") = fid; + register unsigned long x1 asm("x1") = a0; + register unsigned long x2 asm("x2") = a1; + register unsigned long x3 asm("x3") = a2; + register unsigned long x4 asm("x4") = a3; + register unsigned long x5 asm("x5") = a4; + register unsigned long x6 asm("x6") = a5; + + asm volatile ("smc #0\n" + : "+r" (x0), "+r" (x1), "+r" (x2), "+r" (x3), + "+r" (x4), "+r" (x5), "+r" (x6) + : + : "x7", "x8", "x9", "x10", "x11", "x12", + "x13", "x14", "x15", "x16", "x17" ); + + if (ret) { + ret[0] = x0; + ret[1] = x1; + ret[2] = x2; + ret[3] = x3; + } + + return x0; +} + +static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point) +{ + int err; + u32 fn; + + fn = 0xC4000003; + err = call_smcc64(fn, cpuid, entry_point, 0, 0, 0, 0, NULL); + return err; +} + +static int psci_cpu_off(void) +{ + u32 fn; + + fn = 0x84000002; + u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN << + PSCI_0_2_POWER_STATE_TYPE_SHIFT; + + call_smcc64(fn, state, 0, 0, 0, 0, 0, NULL); + + asm volatile("b .\r\n"); + + return 0; +} + +extern void *el2_entry; +extern void *__hyp_stub_vectors; + +struct jailhouse_header *header; +struct jailhouse_system *config_header; +struct jailhouse_system *config; +struct jailhouse_memory *hv_mem; +int i; +static unsigned long hv_core_and_percpu_size; +long max_cpus = 4; +unsigned long config_size; +void *hypervisor_mem; + +int entry(int); +extern void gic_dist_init(void); + +/* Run in EL1 mode */ +static void enter_hypervisor(void *info, int cpu) +{ + struct jailhouse_header *header = info; + int (*entry)(unsigned int); + + entry = header->entry + (unsigned long)hypervisor_mem; + + entry(cpu); +} + +int entry(int cpuid) +{ + /* Jump into Linux Kernel*/ + void (*kernel_entry)(void *fdt_addr, void *res0, void *res1, void *res2); + + /* TODO: refine */ + if (cpuid & 0xff) { + enter_hypervisor(header, cpuid & 0xff); + /* Wait all initialized? */ + /*asm volatile("1: wfi; b 1b\r\n");*/ + asm volatile("wfi;\r\n"); + psci_cpu_off(); + } + + header = (struct jailhouse_header *)JAILHOUSE_ADDR; + config_header = (struct jailhouse_system *)CELL0_CONF_ADDR; + hv_mem = &config_header->hypervisor_memory; + + //max_cpus = get_max_cpus(config_header.root_cell.cpu_set_size, arg); + // + if (memcmp(header->signature, JAILHOUSE_SIGNATURE, sizeof(header->signature)) != 0) + asm volatile("b .\r\n"); + + hv_core_and_percpu_size = header->core_size + + max_cpus * header->percpu_size; + + config_size = jailhouse_system_config_size(config_header); + + if (hv_core_and_percpu_size >= hv_mem->size || + config_size >= hv_mem->size - hv_core_and_percpu_size) + asm volatile("b .\r\n"); + + hypervisor_mem = (void *)config_header->hypervisor_memory.phys_start; + /* Copy hypervisor's binary image at beginning of the memory region + * and clear the rest to zero. */ + memcpy(hypervisor_mem, header, header->console_page); + memset(hypervisor_mem + header->console_page, 0, + hv_mem->size - header->console_page); + + header = (struct jailhouse_header *)hypervisor_mem; + header->max_cpus = max_cpus; + header->arm_linux_hyp_vectors = (unsigned long long)__hyp_stub_vectors; + header->arm_linux_hyp_abi = HYP_STUB_ABI_OPCODE; + + header->online_cpus = 4; + + config = (struct jailhouse_system *)(hypervisor_mem + hv_core_and_percpu_size); + memcpy(config, config_header, config_size); + + /* Init GIC */ + gic_dist_init(); + + /* PSCI CPU ON */ + for (i = 1; i < max_cpus; i++) { + psci_cpu_on(i, LOADER_ADDR); + } + + /* Do we need to enable MMU to let CPU0 check CPU1/2/3/x done jailhouse initialization? */ + /* TODO: refine */ + enter_hypervisor(header, cpuid & 0xff); + + /* Do we need to Wait Secondaries off? */ + /* TODO: not needed? */ + for (i = 1; i < max_cpus; i++) { + //jailhouse not support this. + //psci_cpu_kill(i); + } + + asm volatile("sev;\r\n"); + + /* Create inmate cells */ + /*asm volatile ("b .\r\n");*/ + inmate_cell(); + + /* Add some logic to manage cpus for linux + * need call psci_cpu_off for cpus not used by inmate cell + */ + + kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,void *res2))CELL0_INMATE_ADDR; + + kernel_entry((void *)CELL0_FDT_ADDR, 0, 0, 0); + + /* Not reach here */ + return 0; +} diff --git a/loader/mmio.h b/loader/mmio.h new file mode 100644 index 00000000..d50d20e7 --- /dev/null +++ b/loader/mmio.h @@ -0,0 +1,21 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright 2020 NXP + * + * Authors: + * Peng Fan <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +static inline void writel(u32 value, void *address) +{ + mmio_write32(address, value); +} + +static inline u32 readl(void *address) +{ + return mmio_read32(address); +} diff --git a/loader/psci.h b/loader/psci.h new file mode 100644 index 00000000..2fcad1dd --- /dev/null +++ b/loader/psci.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * ARM Power State and Coordination Interface (PSCI) header + * + * This header holds common PSCI defines and macros shared + * by: ARM kernel, ARM64 kernel, KVM ARM/ARM64 and user space. + * + * Copyright (C) 2014 Linaro Ltd. + * Author: Anup Patel <[email protected]> + */ + +#ifndef _UAPI_LINUX_PSCI_H +#define _UAPI_LINUX_PSCI_H + +/* + * PSCI v0.1 interface + * + * The PSCI v0.1 function numbers are implementation defined. + * + * Only PSCI return values such as: SUCCESS, NOT_SUPPORTED, + * INVALID_PARAMS, and DENIED defined below are applicable + * to PSCI v0.1. + */ + +/* PSCI v0.2 interface */ +#define PSCI_0_2_FN_BASE 0x84000000 +#define PSCI_0_2_FN(n) (PSCI_0_2_FN_BASE + (n)) +#define PSCI_0_2_64BIT 0x40000000 +#define PSCI_0_2_FN64_BASE \ + (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT) +#define PSCI_0_2_FN64(n) (PSCI_0_2_FN64_BASE + (n)) + +#define PSCI_0_2_FN_PSCI_VERSION PSCI_0_2_FN(0) +#define PSCI_0_2_FN_CPU_SUSPEND PSCI_0_2_FN(1) +#define PSCI_0_2_FN_CPU_OFF PSCI_0_2_FN(2) +#define PSCI_0_2_FN_CPU_ON PSCI_0_2_FN(3) +#define PSCI_0_2_FN_AFFINITY_INFO PSCI_0_2_FN(4) +#define PSCI_0_2_FN_MIGRATE PSCI_0_2_FN(5) +#define PSCI_0_2_FN_MIGRATE_INFO_TYPE PSCI_0_2_FN(6) +#define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7) +#define PSCI_0_2_FN_SYSTEM_OFF PSCI_0_2_FN(8) +#define PSCI_0_2_FN_SYSTEM_RESET PSCI_0_2_FN(9) + +#define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1) +#define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3) +#define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4) +#define PSCI_0_2_FN64_MIGRATE PSCI_0_2_FN64(5) +#define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7) + +#define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10) +#define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14) +#define PSCI_1_0_FN_SET_SUSPEND_MODE PSCI_0_2_FN(15) +#define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18) + +#define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14) +#define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18) + +/* PSCI v0.2 power state encoding for CPU_SUSPEND function */ +#define PSCI_0_2_POWER_STATE_ID_MASK 0xffff +#define PSCI_0_2_POWER_STATE_ID_SHIFT 0 +#define PSCI_0_2_POWER_STATE_TYPE_SHIFT 16 +#define PSCI_0_2_POWER_STATE_TYPE_MASK \ + (0x1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT) +#define PSCI_0_2_POWER_STATE_AFFL_SHIFT 24 +#define PSCI_0_2_POWER_STATE_AFFL_MASK \ + (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) + +/* PSCI extended power state encoding for CPU_SUSPEND function */ +#define PSCI_1_0_EXT_POWER_STATE_ID_MASK 0xfffffff +#define PSCI_1_0_EXT_POWER_STATE_ID_SHIFT 0 +#define PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT 30 +#define PSCI_1_0_EXT_POWER_STATE_TYPE_MASK \ + (0x1 << PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT) + +/* PSCI v0.2 affinity level state returned by AFFINITY_INFO */ +#define PSCI_0_2_AFFINITY_LEVEL_ON 0 +#define PSCI_0_2_AFFINITY_LEVEL_OFF 1 +#define PSCI_0_2_AFFINITY_LEVEL_ON_PENDING 2 + +/* PSCI v0.2 multicore support in Trusted OS returned by MIGRATE_INFO_TYPE */ +#define PSCI_0_2_TOS_UP_MIGRATE 0 +#define PSCI_0_2_TOS_UP_NO_MIGRATE 1 +#define PSCI_0_2_TOS_MP 2 + +/* PSCI version decoding (independent of PSCI version) */ +#define PSCI_VERSION_MAJOR_SHIFT 16 +#define PSCI_VERSION_MINOR_MASK \ + ((1U << PSCI_VERSION_MAJOR_SHIFT) - 1) +#define PSCI_VERSION_MAJOR_MASK ~PSCI_VERSION_MINOR_MASK +#define PSCI_VERSION_MAJOR(ver) \ + (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT) +#define PSCI_VERSION_MINOR(ver) \ + ((ver) & PSCI_VERSION_MINOR_MASK) +#define PSCI_VERSION(maj, min) \ + ((((maj) << PSCI_VERSION_MAJOR_SHIFT) & PSCI_VERSION_MAJOR_MASK) | \ + ((min) & PSCI_VERSION_MINOR_MASK)) + +/* PSCI features decoding (>=1.0) */ +#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1 +#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK \ + (0x1 << PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT) + +#define PSCI_1_0_OS_INITIATED BIT(0) +#define PSCI_1_0_SUSPEND_MODE_PC 0 +#define PSCI_1_0_SUSPEND_MODE_OSI 1 + +/* PSCI return values (inclusive of all PSCI versions) */ +#define PSCI_RET_SUCCESS 0 +#define PSCI_RET_NOT_SUPPORTED -1 +#define PSCI_RET_INVALID_PARAMS -2 +#define PSCI_RET_DENIED -3 +#define PSCI_RET_ALREADY_ON -4 +#define PSCI_RET_ON_PENDING -5 +#define PSCI_RET_INTERNAL_FAILURE -6 +#define PSCI_RET_NOT_PRESENT -7 +#define PSCI_RET_DISABLED -8 +#define PSCI_RET_INVALID_ADDRESS -9 + +#endif /* _UAPI_LINUX_PSCI_H */ -- 2.16.4 -- You received this message because you are subscribed to the Google Groups "Jailhouse" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/jailhouse-dev/20200305093950.848-3-peng.fan%40nxp.com.
