arm64: kexec: install a copy of the linear-map

To perform the kexec relocation with the MMU enabled, we need a copy
of the linear map.

Create one, and install it from the relocation code. This has to be done
from the assembly code as it will be idmapped with TTBR0. The kernel
runs in TTRB1, so can't use the break-before-make sequence on the mapping
it is executing from.

The makes no difference yet as the relocation code runs with the MMU
disabled.

Suggested-by: James Morse <james.morse@arm.com>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20210930143113.1502553-12-pasha.tatashin@soleen.com
Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
Pasha Tatashin 2021-09-30 14:31:09 +00:00 коммит произвёл Will Deacon
Родитель 19a046f07c
Коммит 3744b5280e
6 изменённых файлов: 40 добавлений и 22 удалений

Просмотреть файл

@ -483,6 +483,25 @@ alternative_endif
_cond_extable .Licache_op\@, \fixup _cond_extable .Licache_op\@, \fixup
.endm .endm
/*
* To prevent the possibility of old and new partial table walks being visible
* in the tlb, switch the ttbr to a zero page when we invalidate the old
* records. D4.7.1 'General TLB maintenance requirements' in ARM DDI 0487A.i
* Even switching to our copied tables will cause a changed output address at
* each stage of the walk.
*/
.macro break_before_make_ttbr_switch zero_page, page_table, tmp, tmp2
phys_to_ttbr \tmp, \zero_page
msr ttbr1_el1, \tmp
isb
tlbi vmalle1
dsb nsh
phys_to_ttbr \tmp, \page_table
offset_ttbr1 \tmp, \tmp2
msr ttbr1_el1, \tmp
isb
.endm
/* /*
* reset_pmuserenr_el0 - reset PMUSERENR_EL0 if PMUv3 present * reset_pmuserenr_el0 - reset PMUSERENR_EL0 if PMUv3 present
*/ */

Просмотреть файл

@ -97,6 +97,8 @@ struct kimage_arch {
phys_addr_t dtb_mem; phys_addr_t dtb_mem;
phys_addr_t kern_reloc; phys_addr_t kern_reloc;
phys_addr_t el2_vectors; phys_addr_t el2_vectors;
phys_addr_t ttbr1;
phys_addr_t zero_page;
}; };
#ifdef CONFIG_KEXEC_FILE #ifdef CONFIG_KEXEC_FILE

Просмотреть файл

@ -175,6 +175,8 @@ int main(void)
#ifdef CONFIG_KEXEC_CORE #ifdef CONFIG_KEXEC_CORE
DEFINE(KIMAGE_ARCH_DTB_MEM, offsetof(struct kimage, arch.dtb_mem)); DEFINE(KIMAGE_ARCH_DTB_MEM, offsetof(struct kimage, arch.dtb_mem));
DEFINE(KIMAGE_ARCH_EL2_VECTORS, offsetof(struct kimage, arch.el2_vectors)); DEFINE(KIMAGE_ARCH_EL2_VECTORS, offsetof(struct kimage, arch.el2_vectors));
DEFINE(KIMAGE_ARCH_ZERO_PAGE, offsetof(struct kimage, arch.zero_page));
DEFINE(KIMAGE_ARCH_TTBR1, offsetof(struct kimage, arch.ttbr1));
DEFINE(KIMAGE_HEAD, offsetof(struct kimage, head)); DEFINE(KIMAGE_HEAD, offsetof(struct kimage, head));
DEFINE(KIMAGE_START, offsetof(struct kimage, start)); DEFINE(KIMAGE_START, offsetof(struct kimage, start));
BLANK(); BLANK();

Просмотреть файл

@ -15,26 +15,6 @@
#include <asm/page.h> #include <asm/page.h>
#include <asm/virt.h> #include <asm/virt.h>
/*
* To prevent the possibility of old and new partial table walks being visible
* in the tlb, switch the ttbr to a zero page when we invalidate the old
* records. D4.7.1 'General TLB maintenance requirements' in ARM DDI 0487A.i
* Even switching to our copied tables will cause a changed output address at
* each stage of the walk.
*/
.macro break_before_make_ttbr_switch zero_page, page_table, tmp, tmp2
phys_to_ttbr \tmp, \zero_page
msr ttbr1_el1, \tmp
isb
tlbi vmalle1
dsb nsh
phys_to_ttbr \tmp, \page_table
offset_ttbr1 \tmp, \tmp2
msr ttbr1_el1, \tmp
isb
.endm
/* /*
* Resume from hibernate * Resume from hibernate
* *

Просмотреть файл

@ -159,6 +159,8 @@ static void *kexec_page_alloc(void *arg)
int machine_kexec_post_load(struct kimage *kimage) int machine_kexec_post_load(struct kimage *kimage)
{ {
int rc;
pgd_t *trans_pgd;
void *reloc_code = page_to_virt(kimage->control_code_page); void *reloc_code = page_to_virt(kimage->control_code_page);
long reloc_size; long reloc_size;
struct trans_pgd_info info = { struct trans_pgd_info info = {
@ -175,12 +177,22 @@ int machine_kexec_post_load(struct kimage *kimage)
kimage->arch.el2_vectors = 0; kimage->arch.el2_vectors = 0;
if (is_hyp_nvhe()) { if (is_hyp_nvhe()) {
int rc = trans_pgd_copy_el2_vectors(&info, rc = trans_pgd_copy_el2_vectors(&info,
&kimage->arch.el2_vectors); &kimage->arch.el2_vectors);
if (rc) if (rc)
return rc; return rc;
} }
/* Create a copy of the linear map */
trans_pgd = kexec_page_alloc(kimage);
if (!trans_pgd)
return -ENOMEM;
rc = trans_pgd_create_copy(&info, &trans_pgd, PAGE_OFFSET, PAGE_END);
if (rc)
return rc;
kimage->arch.ttbr1 = __pa(trans_pgd);
kimage->arch.zero_page = __pa(empty_zero_page);
reloc_size = __relocate_new_kernel_end - __relocate_new_kernel_start; reloc_size = __relocate_new_kernel_end - __relocate_new_kernel_start;
memcpy(reloc_code, __relocate_new_kernel_start, reloc_size); memcpy(reloc_code, __relocate_new_kernel_start, reloc_size);
kimage->arch.kern_reloc = __pa(reloc_code); kimage->arch.kern_reloc = __pa(reloc_code);

Просмотреть файл

@ -29,10 +29,13 @@
*/ */
SYM_CODE_START(arm64_relocate_new_kernel) SYM_CODE_START(arm64_relocate_new_kernel)
/* Setup the list loop variables. */ /* Setup the list loop variables. */
ldr x18, [x0, #KIMAGE_ARCH_ZERO_PAGE] /* x18 = zero page for BBM */
ldr x17, [x0, #KIMAGE_ARCH_TTBR1] /* x17 = linear map copy */
ldr x16, [x0, #KIMAGE_HEAD] /* x16 = kimage_head */ ldr x16, [x0, #KIMAGE_HEAD] /* x16 = kimage_head */
mov x14, xzr /* x14 = entry ptr */ mov x14, xzr /* x14 = entry ptr */
mov x13, xzr /* x13 = copy dest */ mov x13, xzr /* x13 = copy dest */
raw_dcache_line_size x15, x1 /* x15 = dcache line size */ raw_dcache_line_size x15, x1 /* x15 = dcache line size */
break_before_make_ttbr_switch x18, x17, x1, x2 /* set linear map */
.Lloop: .Lloop:
and x12, x16, PAGE_MASK /* x12 = addr */ and x12, x16, PAGE_MASK /* x12 = addr */