mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
ANDROID: KVM: arm64: Resolve hyp module addresses using ELF sections
Resolving the addresses of the hypervisor sections within a loadable module using symbol assignment is fragile, particularly in the face of mergeable sections (i.e. those emitted with SHF_MERGE by the compiler). Instead, parse the ELF .hyp.* sections directly and remove the need for global symbols in the hypervisor module linker script. Signed-off-by: Will Deacon <will@kernel.org> [will: Preserve function_nocfi() calls which aren't needed in android14-6.1] Signed-off-by: Will Deacon <willdeacon@google.com> Bug: 261855285 Change-Id: I91d88e1a341b91ffe52ffc770dddc9b46ccb3aa4
This commit is contained in:
@@ -141,6 +141,7 @@ config ARM64
|
|||||||
select GENERIC_VDSO_TIME_NS
|
select GENERIC_VDSO_TIME_NS
|
||||||
select HANDLE_DOMAIN_IRQ
|
select HANDLE_DOMAIN_IRQ
|
||||||
select HARDIRQS_SW_RESEND
|
select HARDIRQS_SW_RESEND
|
||||||
|
select HAVE_MOD_ARCH_SPECIFIC if (ARM64_MODULE_PLTS || KVM)
|
||||||
select HAVE_MOVE_PMD
|
select HAVE_MOVE_PMD
|
||||||
select HAVE_MOVE_PUD
|
select HAVE_MOVE_PUD
|
||||||
select HAVE_PCI
|
select HAVE_PCI
|
||||||
@@ -1933,7 +1934,6 @@ config ARM64_SVE
|
|||||||
config ARM64_MODULE_PLTS
|
config ARM64_MODULE_PLTS
|
||||||
bool "Use PLTs to allow module memory to spill over into vmalloc area"
|
bool "Use PLTs to allow module memory to spill over into vmalloc area"
|
||||||
depends on MODULES
|
depends on MODULES
|
||||||
select HAVE_MOD_ARCH_SPECIFIC
|
|
||||||
help
|
help
|
||||||
Allocate PLTs when loading modules so that jumps and calls whose
|
Allocate PLTs when loading modules so that jumps and calls whose
|
||||||
targets are too far away for their relative offsets to be encoded
|
targets are too far away for their relative offsets to be encoded
|
||||||
|
|||||||
@@ -122,9 +122,6 @@ void kvm_update_va_mask(struct alt_instr *alt,
|
|||||||
__le32 *origptr, __le32 *updptr, int nr_inst);
|
__le32 *origptr, __le32 *updptr, int nr_inst);
|
||||||
void kvm_compute_layout(void);
|
void kvm_compute_layout(void);
|
||||||
void kvm_apply_hyp_relocations(void);
|
void kvm_apply_hyp_relocations(void);
|
||||||
void kvm_apply_hyp_module_relocations(void *mod_start, void *hyp_va,
|
|
||||||
kvm_nvhe_reloc_t *begin,
|
|
||||||
kvm_nvhe_reloc_t *end);
|
|
||||||
|
|
||||||
#define __hyp_pa(x) (((phys_addr_t)(x)) + hyp_physvirt_offset)
|
#define __hyp_pa(x) (((phys_addr_t)(x)) + hyp_physvirt_offset)
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ enum pkvm_psci_notification {
|
|||||||
PKVM_PSCI_CPU_ENTRY,
|
PKVM_PSCI_CPU_ENTRY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_MODULES
|
||||||
struct pkvm_module_ops {
|
struct pkvm_module_ops {
|
||||||
int (*create_private_mapping)(phys_addr_t phys, size_t size,
|
int (*create_private_mapping)(phys_addr_t phys, size_t size,
|
||||||
enum kvm_pgtable_prot prot,
|
enum kvm_pgtable_prot prot,
|
||||||
@@ -41,28 +42,24 @@ struct pkvm_module_ops {
|
|||||||
unsigned long (*kern_hyp_va)(unsigned long x);
|
unsigned long (*kern_hyp_va)(unsigned long x);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pkvm_module_section {
|
int __pkvm_load_el2_module(struct module *this, unsigned long *token);
|
||||||
void *start;
|
|
||||||
void *end;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef s32 kvm_nvhe_reloc_t;
|
|
||||||
|
|
||||||
struct pkvm_el2_module {
|
|
||||||
struct pkvm_module_section text;
|
|
||||||
struct pkvm_module_section bss;
|
|
||||||
struct pkvm_module_section rodata;
|
|
||||||
struct pkvm_module_section data;
|
|
||||||
kvm_nvhe_reloc_t *relocs;
|
|
||||||
unsigned int nr_relocs;
|
|
||||||
int (*init)(const struct pkvm_module_ops *ops);
|
|
||||||
};
|
|
||||||
|
|
||||||
int __pkvm_load_el2_module(struct pkvm_el2_module *mod, struct module *this,
|
|
||||||
unsigned long *token);
|
|
||||||
|
|
||||||
int __pkvm_register_el2_call(dyn_hcall_t hfn, unsigned long token,
|
int __pkvm_register_el2_call(dyn_hcall_t hfn, unsigned long token,
|
||||||
unsigned long hyp_text_kern_va);
|
unsigned long hyp_text_kern_va);
|
||||||
|
#else
|
||||||
|
static inline int __pkvm_load_el2_module(struct module *this,
|
||||||
|
unsigned long *token)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int __pkvm_register_el2_call(dyn_hcall_t hfn, unsigned long token,
|
||||||
|
unsigned long hyp_text_kern_va)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_MODULES */
|
||||||
|
|
||||||
#ifdef MODULE
|
#ifdef MODULE
|
||||||
/*
|
/*
|
||||||
* function_nocfi() does not work with function pointers, hence the macro in
|
* function_nocfi() does not work with function pointers, hence the macro in
|
||||||
@@ -70,39 +67,14 @@ int __pkvm_register_el2_call(dyn_hcall_t hfn, unsigned long token,
|
|||||||
*/
|
*/
|
||||||
#define pkvm_load_el2_module(init_fn, token) \
|
#define pkvm_load_el2_module(init_fn, token) \
|
||||||
({ \
|
({ \
|
||||||
extern char __kvm_nvhe___hypmod_text_start[]; \
|
THIS_MODULE->arch.hyp.init = function_nocfi(init_fn); \
|
||||||
extern char __kvm_nvhe___hypmod_text_end[]; \
|
__pkvm_load_el2_module(THIS_MODULE, token); \
|
||||||
extern char __kvm_nvhe___hypmod_bss_start[]; \
|
|
||||||
extern char __kvm_nvhe___hypmod_bss_end[]; \
|
|
||||||
extern char __kvm_nvhe___hypmod_rodata_start[]; \
|
|
||||||
extern char __kvm_nvhe___hypmod_rodata_end[]; \
|
|
||||||
extern char __kvm_nvhe___hypmod_data_start[]; \
|
|
||||||
extern char __kvm_nvhe___hypmod_data_end[]; \
|
|
||||||
extern char __kvm_nvhe___hyprel_start[]; \
|
|
||||||
extern char __kvm_nvhe___hyprel_end[]; \
|
|
||||||
struct pkvm_el2_module mod; \
|
|
||||||
\
|
|
||||||
mod.text.start = __kvm_nvhe___hypmod_text_start; \
|
|
||||||
mod.text.end = __kvm_nvhe___hypmod_text_end; \
|
|
||||||
mod.bss.start = __kvm_nvhe___hypmod_bss_start; \
|
|
||||||
mod.bss.end = __kvm_nvhe___hypmod_bss_end; \
|
|
||||||
mod.rodata.start = __kvm_nvhe___hypmod_rodata_start; \
|
|
||||||
mod.rodata.end = __kvm_nvhe___hypmod_rodata_end; \
|
|
||||||
mod.data.start = __kvm_nvhe___hypmod_data_start; \
|
|
||||||
mod.data.end = __kvm_nvhe___hypmod_data_end; \
|
|
||||||
mod.relocs = (kvm_nvhe_reloc_t *)__kvm_nvhe___hyprel_start; \
|
|
||||||
mod.nr_relocs = (__kvm_nvhe___hyprel_end - __kvm_nvhe___hyprel_start) / \
|
|
||||||
sizeof(*mod.relocs); \
|
|
||||||
mod.init = function_nocfi(init_fn); \
|
|
||||||
\
|
|
||||||
__pkvm_load_el2_module(&mod, THIS_MODULE, token); \
|
|
||||||
})
|
})
|
||||||
|
|
||||||
#define pkvm_register_el2_mod_call(hfn, token) \
|
#define pkvm_register_el2_mod_call(hfn, token) \
|
||||||
({ \
|
({ \
|
||||||
extern char __kvm_nvhe___hypmod_text_start[]; \
|
unsigned long hyp_text_kern_va; \
|
||||||
unsigned long hyp_text_kern_va = \
|
hyp_text_kern_va = THIS_MODULE->arch.hyp.text.start; \
|
||||||
(unsigned long)__kvm_nvhe___hypmod_text_start; \
|
|
||||||
__pkvm_register_el2_call(function_nocfi(hfn), token, \
|
__pkvm_register_el2_call(function_nocfi(hfn), token, \
|
||||||
hyp_text_kern_va); \
|
hyp_text_kern_va); \
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,12 +14,50 @@ struct mod_plt_sec {
|
|||||||
int plt_max_entries;
|
int plt_max_entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mod_arch_specific {
|
#define ARM64_MODULE_PLTS_ARCHDATA \
|
||||||
struct mod_plt_sec core;
|
struct mod_plt_sec core; \
|
||||||
struct mod_plt_sec init;
|
struct mod_plt_sec init; \
|
||||||
|
\
|
||||||
/* for CONFIG_DYNAMIC_FTRACE */
|
/* for CONFIG_DYNAMIC_FTRACE */ \
|
||||||
struct plt_entry *ftrace_trampolines;
|
struct plt_entry *ftrace_trampolines;
|
||||||
|
#else
|
||||||
|
#define ARM64_MODULE_PLTS_ARCHDATA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM
|
||||||
|
struct pkvm_module_section {
|
||||||
|
void *start;
|
||||||
|
void *end;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef s32 kvm_nvhe_reloc_t;
|
||||||
|
struct pkvm_module_ops;
|
||||||
|
|
||||||
|
struct pkvm_el2_module {
|
||||||
|
struct pkvm_module_section text;
|
||||||
|
struct pkvm_module_section bss;
|
||||||
|
struct pkvm_module_section rodata;
|
||||||
|
struct pkvm_module_section data;
|
||||||
|
kvm_nvhe_reloc_t *relocs;
|
||||||
|
unsigned int nr_relocs;
|
||||||
|
int (*init)(const struct pkvm_module_ops *ops);
|
||||||
|
};
|
||||||
|
|
||||||
|
void kvm_apply_hyp_module_relocations(void *mod_start, void *hyp_va,
|
||||||
|
kvm_nvhe_reloc_t *begin,
|
||||||
|
kvm_nvhe_reloc_t *end);
|
||||||
|
|
||||||
|
#define ARM64_MODULE_KVM_ARCHDATA \
|
||||||
|
/* For pKVM hypervisor modules */ \
|
||||||
|
struct pkvm_el2_module hyp;
|
||||||
|
#else
|
||||||
|
#define ARM64_MODULE_KVM_ARCHDATA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAVE_MOD_ARCH_SPECIFIC
|
||||||
|
struct mod_arch_specific {
|
||||||
|
ARM64_MODULE_PLTS_ARCHDATA
|
||||||
|
ARM64_MODULE_KVM_ARCHDATA
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -520,14 +520,72 @@ static int module_init_ftrace_plt(const Elf_Ehdr *hdr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int module_init_hyp(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
||||||
|
struct module *mod)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KVM
|
||||||
|
const Elf_Shdr *s;
|
||||||
|
|
||||||
|
s = find_section(hdr, sechdrs, ".hyp.text");
|
||||||
|
if (!s)
|
||||||
|
return -ENOEXEC;
|
||||||
|
|
||||||
|
mod->arch.hyp.text = (struct pkvm_module_section) {
|
||||||
|
.start = (void *)s->sh_addr,
|
||||||
|
.end = (void *)s->sh_addr + s->sh_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
s = find_section(hdr, sechdrs, ".hyp.bss");
|
||||||
|
if (!s)
|
||||||
|
return -ENOEXEC;
|
||||||
|
|
||||||
|
mod->arch.hyp.bss = (struct pkvm_module_section) {
|
||||||
|
.start = (void *)s->sh_addr,
|
||||||
|
.end = (void *)s->sh_addr + s->sh_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
s = find_section(hdr, sechdrs, ".hyp.rodata");
|
||||||
|
if (!s)
|
||||||
|
return -ENOEXEC;
|
||||||
|
|
||||||
|
mod->arch.hyp.rodata = (struct pkvm_module_section) {
|
||||||
|
.start = (void *)s->sh_addr,
|
||||||
|
.end = (void *)s->sh_addr + s->sh_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
s = find_section(hdr, sechdrs, ".hyp.data");
|
||||||
|
if (!s)
|
||||||
|
return -ENOEXEC;
|
||||||
|
|
||||||
|
mod->arch.hyp.data = (struct pkvm_module_section) {
|
||||||
|
.start = (void *)s->sh_addr,
|
||||||
|
.end = (void *)s->sh_addr + s->sh_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
s = find_section(hdr, sechdrs, ".hyp.reloc");
|
||||||
|
if (!s)
|
||||||
|
return -ENOEXEC;
|
||||||
|
|
||||||
|
mod->arch.hyp.relocs = (void *)s->sh_addr;
|
||||||
|
mod->arch.hyp.nr_relocs = s->sh_size / sizeof(mod->arch.hyp.relocs);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int module_finalize(const Elf_Ehdr *hdr,
|
int module_finalize(const Elf_Ehdr *hdr,
|
||||||
const Elf_Shdr *sechdrs,
|
const Elf_Shdr *sechdrs,
|
||||||
struct module *me)
|
struct module *me)
|
||||||
{
|
{
|
||||||
|
int err;
|
||||||
const Elf_Shdr *s;
|
const Elf_Shdr *s;
|
||||||
|
|
||||||
s = find_section(hdr, sechdrs, ".altinstructions");
|
s = find_section(hdr, sechdrs, ".altinstructions");
|
||||||
if (s)
|
if (s)
|
||||||
apply_alternatives_module((void *)s->sh_addr, s->sh_size);
|
apply_alternatives_module((void *)s->sh_addr, s->sh_size);
|
||||||
|
|
||||||
return module_init_ftrace_plt(hdr, sechdrs, me);
|
err = module_init_ftrace_plt(hdr, sechdrs, me);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return module_init_hyp(hdr, sechdrs, me);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,32 +6,21 @@
|
|||||||
SECTIONS {
|
SECTIONS {
|
||||||
.hyp.text : {
|
.hyp.text : {
|
||||||
HYP_SECTION_SYMBOL_NAME(.text) = .;
|
HYP_SECTION_SYMBOL_NAME(.text) = .;
|
||||||
__hypmod_text_start = .;
|
|
||||||
*(.text .text.*)
|
*(.text .text.*)
|
||||||
__hypmod_text_end = .;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hyp.bss : {
|
.hyp.bss : {
|
||||||
HYP_SECTION_SYMBOL_NAME(.bss) = .;
|
HYP_SECTION_SYMBOL_NAME(.bss) = .;
|
||||||
__hypmod_bss_start = .;
|
|
||||||
*(.bss .bss.*)
|
*(.bss .bss.*)
|
||||||
FILL(0)
|
|
||||||
__hypmod_bss_end = .;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hyp.rodata : {
|
.hyp.rodata : {
|
||||||
HYP_SECTION_SYMBOL_NAME(.rodata) = .;
|
HYP_SECTION_SYMBOL_NAME(.rodata) = .;
|
||||||
__hypmod_rodata_start = .;
|
*(.rodata .rodata.*)
|
||||||
*(.rodata .rodata.* .note.gnu.property)
|
|
||||||
BYTE(0)
|
|
||||||
__hypmod_rodata_end = .;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hyp.data : {
|
.hyp.data : {
|
||||||
HYP_SECTION_SYMBOL_NAME(.data) = .;
|
HYP_SECTION_SYMBOL_NAME(.data) = .;
|
||||||
__hypmod_data_start = .;
|
|
||||||
*(.data .data.*)
|
*(.data .data.*)
|
||||||
BYTE(0)
|
|
||||||
__hypmod_data_end = .;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,12 @@ static void pkvm_psci_notify(enum pkvm_psci_notification notif, struct kvm_cpu_c
|
|||||||
pkvm_psci_notifier(notif, host_ctxt);
|
pkvm_psci_notifier(notif, host_ctxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_MODULES
|
||||||
int __pkvm_register_psci_notifier(void (*cb)(enum pkvm_psci_notification, struct kvm_cpu_context *))
|
int __pkvm_register_psci_notifier(void (*cb)(enum pkvm_psci_notification, struct kvm_cpu_context *))
|
||||||
{
|
{
|
||||||
return cmpxchg(&pkvm_psci_notifier, NULL, cb) ? -EBUSY : 0;
|
return cmpxchg(&pkvm_psci_notifier, NULL, cb) ? -EBUSY : 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define INVALID_CPU_ID UINT_MAX
|
#define INVALID_CPU_ID UINT_MAX
|
||||||
|
|
||||||
|
|||||||
@@ -507,7 +507,6 @@ static int __init early_pkvm_enable_modules(char *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
early_param("kvm-arm.protected_modules", early_pkvm_enable_modules);
|
early_param("kvm-arm.protected_modules", early_pkvm_enable_modules);
|
||||||
#endif
|
|
||||||
|
|
||||||
struct pkvm_mod_sec_mapping {
|
struct pkvm_mod_sec_mapping {
|
||||||
struct pkvm_module_section *sec;
|
struct pkvm_module_section *sec;
|
||||||
@@ -584,9 +583,9 @@ static int __pkvm_cmp_mod_sec(const void *p1, const void *p2)
|
|||||||
return s1->sec->start < s2->sec->start ? -1 : s1->sec->start > s2->sec->start;
|
return s1->sec->start < s2->sec->start ? -1 : s1->sec->start > s2->sec->start;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __pkvm_load_el2_module(struct pkvm_el2_module *mod, struct module *this,
|
int __pkvm_load_el2_module(struct module *this, unsigned long *token)
|
||||||
unsigned long *token)
|
|
||||||
{
|
{
|
||||||
|
struct pkvm_el2_module *mod = &this->arch.hyp;
|
||||||
struct pkvm_mod_sec_mapping secs_map[] = {
|
struct pkvm_mod_sec_mapping secs_map[] = {
|
||||||
{ &mod->text, KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_X },
|
{ &mod->text, KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_X },
|
||||||
{ &mod->bss, KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_W },
|
{ &mod->bss, KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_W },
|
||||||
@@ -616,7 +615,7 @@ int __pkvm_load_el2_module(struct pkvm_el2_module *mod, struct module *this,
|
|||||||
sort(secs_map, ARRAY_SIZE(secs_map), sizeof(secs_map[0]), __pkvm_cmp_mod_sec, NULL);
|
sort(secs_map, ARRAY_SIZE(secs_map), sizeof(secs_map[0]), __pkvm_cmp_mod_sec, NULL);
|
||||||
start = secs_map[0].sec->start;
|
start = secs_map[0].sec->start;
|
||||||
end = secs_map[ARRAY_SIZE(secs_map) - 1].sec->end;
|
end = secs_map[ARRAY_SIZE(secs_map) - 1].sec->end;
|
||||||
size = PAGE_ALIGN(end - start);
|
size = end - start;
|
||||||
|
|
||||||
hyp_va = (void *)kvm_call_hyp_nvhe(__pkvm_alloc_module_va, size >> PAGE_SHIFT);
|
hyp_va = (void *)kvm_call_hyp_nvhe(__pkvm_alloc_module_va, size >> PAGE_SHIFT);
|
||||||
if (!hyp_va) {
|
if (!hyp_va) {
|
||||||
@@ -670,3 +669,4 @@ int __pkvm_register_el2_call(dyn_hcall_t hfn, unsigned long token,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__pkvm_register_el2_call);
|
EXPORT_SYMBOL_GPL(__pkvm_register_el2_call);
|
||||||
|
#endif /* CONFIG_MODULES */
|
||||||
|
|||||||
Reference in New Issue
Block a user