@@ -1,5 +1,24 @@
#if !defined(KEXEC_MMU_H)
#define KEXEC_MMU_H
+
+#define SCTLR_ELx_I (1 << 12)
+#define SCTLR_ELx_C (1 << 2)
+#define SCTLR_ELx_M (1 << 0)
+#define SCTLR_ELx_FLAGS (SCTLR_ELx_M | SCTLR_ELx_C | SCTLR_ELx_I)
+#define TCR_SHARED_NONE (0 << 12)
+#define TCR_ORGN_WBWA (1 << 10)
+#define TCR_IRGN_WBWA (1 << 8)
+#define TCR_T0SZ_48 16
+#define TCR_TG0_4K (0 << 14)
+#define TCR_FLAGS (TCR_SHARED_NONE | TCR_ORGN_WBWA |\
+ TCR_IRGN_WBWA | TCR_T0SZ_48 | TCR_TG0_4K)
+#define TCR_IPS_EL1_SHIFT 32
+#define TCR_IPS_EL2_SHIFT 16
+#define ID_AA64MMFR0_TGRAN4_SHIFT 28
+#define ID_AA64MMFR0_PARANGE_MASK 0xF
+#define MT_NORMAL 0
+#define MEMORY_ATTRIBUTES (0xFF << (MT_NORMAL*8))
+
/*
* kexec creates identity page table to be used in purgatory so that
* dcache verification becomes faster.
@@ -26,7 +45,6 @@
#define PMD_TYPE_SECT (1UL << 0)
#define PMD_SECT_AF (1UL << 10)
#define PMD_ATTRINDX(t) ((unsigned long)(t) << 2)
-#define MT_NORMAL 0
#define PMD_FLAGS_NORMAL (PMD_TYPE_SECT | PMD_SECT_AF)
#define MMU_FLAGS_NORMAL (PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS_NORMAL)
#define SECTION_SHIFT PMD_SHIFT
@@ -11,6 +11,7 @@ arm64_PURGATORY_EXTRA_CFLAGS = \
arm64_PURGATORY_SRCS += \
purgatory/arch/arm64/entry.S \
+ purgatory/arch/arm64/cache.S \
purgatory/arch/arm64/purgatory-arm64.c
dist += \
new file mode 100644
@@ -0,0 +1,264 @@
+/*
+ * Some of the routines have been copied from Linux Kernel, therefore
+ * copying the license as well.
+ *
+ * Copyright (C) 2001 Deep Blue Solutions Ltd.
+ * Copyright (C) 2012 ARM Ltd.
+ * Copyright (C) 2016 Pratyush Anand <panand@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "../../../kexec/arch/arm64/kexec-mmu.h"
+/*
+ * dcache_line_size - get the minimum D-cache line size from the CTR register.
+ */
+ .macro dcache_line_size, reg, tmp
+ mrs \tmp, ctr_el0 // read CTR
+ ubfm \tmp, \tmp, #16, #19 // cache line size encoding
+ mov \reg, #4 // bytes per word
+ lsl \reg, \reg, \tmp // actual cache line size
+ .endm
+
+/*
+ * flush_dcache_range(start, end)
+ * - x0 - start - start address of region
+ * - x1 - end - end address of region
+ *
+ */
+flush_dcache_range:
+ dcache_line_size x2, x3
+ sub x3, x2, #1
+ bic x0, x0, x3
+1: dc civac, x0 // clean & invalidate D line / unified line
+ add x0, x0, x2
+ cmp x0, x1
+ b.lo 1b
+ dsb sy
+ ret
+
+/*
+ * invalidate_tlbs_el1()
+ */
+invalidate_tlbs_el1:
+ dsb nshst
+ tlbi vmalle1
+ dsb nsh
+ isb
+ ret
+
+/*
+ * invalidate_tlbs_el2()
+ */
+invalidate_tlbs_el2:
+ dsb nshst
+ tlbi alle2
+ dsb nsh
+ isb
+ ret
+/*
+ * is_4k_page_not_supported - return nonzero if 4k page is not supported
+ */
+is_4k_page_not_supported:
+ mrs x0, ID_AA64MMFR0_EL1
+ and x0, x0, #(0xF << ID_AA64MMFR0_TGRAN4_SHIFT)
+ ret
+
+/*
+ * get_ips_bits - return supported IPS bits
+ */
+get_ips_bits:
+ mrs x0, ID_AA64MMFR0_EL1
+ and x0, x0, #ID_AA64MMFR0_PARANGE_MASK
+ ret
+
+/*
+ * get_current_el - Get information about current exception level
+ */
+get_current_el:
+ mrs x0, CurrentEL
+ lsr x0, x0, #2
+ ret
+
+/*
+ * invalidate_icache - Invalidate I-cache
+ */
+invalidate_icache:
+ ic iallu
+ dsb nsh
+ isb
+ ret
+
+/*
+ * set_mair_tcr_ttbr_sctlr_el1(page_table, tcr_flags) - sets MAIR, TCR , TTBR and SCTLR registers
+ * x0 - page_table - Page Table Base
+ * x1 - tcr_flags - TCR Flags to be set
+ */
+set_mair_tcr_ttbr_sctlr_el1:
+ ldr x2, =MEMORY_ATTRIBUTES
+ msr mair_el1, x2
+ msr tcr_el1, x1
+ msr ttbr0_el1, x0
+ isb
+ mrs x0, sctlr_el1
+ ldr x3, =SCTLR_ELx_FLAGS
+ orr x0, x0, x3
+ msr sctlr_el1, x0
+ isb
+ ret
+
+/*
+ * set_mair_tcr_ttbr_sctlr_el2(page_table, tcr_flags) - sets MAIR, TCR , TTBR and SCTLR registers
+ * x0 - page_table - Page Table Base
+ * x1 - tcr_flags - TCR Flags to be set
+ */
+set_mair_tcr_ttbr_sctlr_el2:
+ ldr x2, =MEMORY_ATTRIBUTES
+ msr mair_el2, x2
+ msr tcr_el2, x1
+ msr ttbr0_el2, x0
+ isb
+ mrs x0, sctlr_el2
+ ldr x3, =SCTLR_ELx_FLAGS
+ orr x0, x0, x3
+ msr sctlr_el2, x0
+ isb
+ ret
+
+/*
+ * reset_sctlr_el1 - disables cache and mmu
+ */
+reset_sctlr_el1:
+ mrs x0, sctlr_el1
+ bic x0, x0, #SCTLR_ELx_C
+ bic x0, x0, #SCTLR_ELx_M
+ msr sctlr_el1, x0
+ isb
+ ret
+
+/*
+ * reset_sctlr_el2 - disables cache and mmu
+ */
+reset_sctlr_el2:
+ mrs x0, sctlr_el2
+ bic x0, x0, #SCTLR_ELx_C
+ bic x0, x0, #SCTLR_ELx_M
+ msr sctlr_el2, x0
+ isb
+ ret
+
+.globl enable_dcache
+/*
+ * x6 - pgtble_base
+ * x7 - tcr_flags
+ * x8 - current_el
+ */
+enable_dcache:
+ stp x29, x30, [sp,#-16]!
+ stp x6, x7, [sp,#-16]!
+ stp x8, x9, [sp,#-16]!
+ bl is_4k_page_not_supported
+ cmp x0, #0
+ b.ne 1f
+ ldr x6, pgtble_base
+ ldr x7, =TCR_FLAGS
+ bl get_current_el
+ mov x8, x0
+ cmp x8, #2
+ b.ne 2f
+ bl invalidate_tlbs_el2
+ bl get_ips_bits
+ lsl x1, x0, #TCR_IPS_EL2_SHIFT
+ orr x1, x1, x7
+ mov x0, x6
+ bl set_mair_tcr_ttbr_sctlr_el2
+ b 1f
+2:
+ cmp x8, #1
+ b.ne 1f
+ bl invalidate_tlbs_el1
+ bl get_ips_bits
+ lsl x1, x0, #TCR_IPS_EL1_SHIFT
+ orr x1, x1, x7
+ mov x0, x6
+ bl set_mair_tcr_ttbr_sctlr_el1
+1:
+ ldp x8, x9, [sp],#16
+ ldp x6, x7, [sp],#16
+ ldp x29, x30, [sp],#16
+ ret
+
+.extern sha256_regions
+.globl disable_dcache
+/*
+ * x6 - pgtble_base
+ * x7 - current_el
+ */
+disable_dcache:
+ stp x29, x30, [sp,#-16]!
+ stp x6, x7, [sp,#-16]!
+ bl is_4k_page_not_supported
+ cmp x0, #0
+ b.ne 1f
+ ldr x6, pgtble_base
+ bl get_current_el
+ mov x7, x0
+ cmp x7, #2
+ b.ne 2f
+ bl reset_sctlr_el2
+ b 3f
+2:
+ cmp x7, #1
+ b.ne 1f
+ bl reset_sctlr_el1
+3:
+ /*
+ * we can only branch to function which does not use stack, until
+ * all memories are flushed.
+ */
+ bl invalidate_icache
+ /* flush d cache for purgatory region */
+ ldr x0, purgatory_base
+ ldr x1, purgatory_len
+ add x1, x1, x0
+ bl flush_dcache_range
+ /* flush d cache for rest of the regions */
+ ldr x6, =sha256_regions
+4:
+ ldp x0, x1, [x6],#16
+ cmp x1, #0
+ b.eq 1f
+ add x1, x1, x0
+ bl flush_dcache_range
+ b 4b
+1:
+ ldp x6, x7, [sp],#16
+ ldp x29, x30, [sp],#16
+ ret
+
+.align 3
+
+.globl pgtble_base
+pgtble_base:
+ .quad 0
+ .size pgtble_base, .-pgtble_base
+
+.globl purgatory_base
+purgatory_base:
+ .quad 0
+ .size purgatory_base, .-purgatory_base
+
+.globl purgatory_len
+purgatory_len:
+ .quad 0
+ .size purgatory_len, .-purgatory_len
@@ -5,6 +5,9 @@
#include <stdint.h>
#include <purgatory.h>
+void enable_dcache(void);
+void disable_dcache(void);
+
void putchar(int ch)
{
/* Nothing for now */
@@ -12,8 +15,10 @@ void putchar(int ch)
void post_verification_setup_arch(void)
{
+ disable_dcache();
}
void setup_arch(void)
{
+ enable_dcache();
}
If a platform supports 4K page table then enable D-cache in purgatory before SHA verification. Disable it before switching to kernel. Signed-off-by: Pratyush Anand <panand@redhat.com> --- kexec/arch/arm64/kexec-mmu.h | 20 ++- purgatory/arch/arm64/Makefile | 1 + purgatory/arch/arm64/cache.S | 264 +++++++++++++++++++++++++++++++++ purgatory/arch/arm64/purgatory-arm64.c | 5 + 4 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 purgatory/arch/arm64/cache.S