xref: /linux/arch/loongarch/include/asm/kfence.h (revision cf6d429eb6563185919322205a320c3b12d1c255)
16ad3df56SEnze Li /* SPDX-License-Identifier: GPL-2.0 */
26ad3df56SEnze Li /*
36ad3df56SEnze Li  * KFENCE support for LoongArch.
46ad3df56SEnze Li  *
56ad3df56SEnze Li  * Author: Enze Li <lienze@kylinos.cn>
66ad3df56SEnze Li  * Copyright (C) 2022-2023 KylinSoft Corporation.
76ad3df56SEnze Li  */
86ad3df56SEnze Li 
96ad3df56SEnze Li #ifndef _ASM_LOONGARCH_KFENCE_H
106ad3df56SEnze Li #define _ASM_LOONGARCH_KFENCE_H
116ad3df56SEnze Li 
126ad3df56SEnze Li #include <linux/kfence.h>
130069455bSKent Overstreet #include <linux/vmalloc.h>
146ad3df56SEnze Li #include <asm/pgtable.h>
156ad3df56SEnze Li #include <asm/tlb.h>
166ad3df56SEnze Li 
arch_kfence_init_pool(void)176ad3df56SEnze Li static inline bool arch_kfence_init_pool(void)
186ad3df56SEnze Li {
196ad3df56SEnze Li 	int err;
200ca84aeaSHuacai Chen 	char *kaddr, *vaddr;
216ad3df56SEnze Li 	char *kfence_pool = __kfence_pool;
226ad3df56SEnze Li 	struct vm_struct *area;
236ad3df56SEnze Li 
246ad3df56SEnze Li 	area = __get_vm_area_caller(KFENCE_POOL_SIZE, VM_IOREMAP,
256ad3df56SEnze Li 				    KFENCE_AREA_START, KFENCE_AREA_END,
266ad3df56SEnze Li 				    __builtin_return_address(0));
276ad3df56SEnze Li 	if (!area)
286ad3df56SEnze Li 		return false;
296ad3df56SEnze Li 
306ad3df56SEnze Li 	__kfence_pool = (char *)area->addr;
316ad3df56SEnze Li 	err = ioremap_page_range((unsigned long)__kfence_pool,
326ad3df56SEnze Li 				 (unsigned long)__kfence_pool + KFENCE_POOL_SIZE,
336ad3df56SEnze Li 				 virt_to_phys((void *)kfence_pool), PAGE_KERNEL);
346ad3df56SEnze Li 	if (err) {
356ad3df56SEnze Li 		free_vm_area(area);
366ad3df56SEnze Li 		__kfence_pool = kfence_pool;
376ad3df56SEnze Li 		return false;
386ad3df56SEnze Li 	}
396ad3df56SEnze Li 
400ca84aeaSHuacai Chen 	kaddr = kfence_pool;
410ca84aeaSHuacai Chen 	vaddr = __kfence_pool;
420ca84aeaSHuacai Chen 	while (kaddr < kfence_pool + KFENCE_POOL_SIZE) {
430ca84aeaSHuacai Chen 		set_page_address(virt_to_page(kaddr), vaddr);
440ca84aeaSHuacai Chen 		kaddr += PAGE_SIZE;
450ca84aeaSHuacai Chen 		vaddr += PAGE_SIZE;
460ca84aeaSHuacai Chen 	}
470ca84aeaSHuacai Chen 
486ad3df56SEnze Li 	return true;
496ad3df56SEnze Li }
506ad3df56SEnze Li 
516ad3df56SEnze Li /* Protect the given page and flush TLB. */
kfence_protect_page(unsigned long addr,bool protect)526ad3df56SEnze Li static inline bool kfence_protect_page(unsigned long addr, bool protect)
536ad3df56SEnze Li {
546ad3df56SEnze Li 	pte_t *pte = virt_to_kpte(addr);
556ad3df56SEnze Li 
56*4574815aSHuacai Chen 	if (WARN_ON(!pte) || pte_none(ptep_get(pte)))
576ad3df56SEnze Li 		return false;
586ad3df56SEnze Li 
596ad3df56SEnze Li 	if (protect)
60*4574815aSHuacai Chen 		set_pte(pte, __pte(pte_val(ptep_get(pte)) & ~(_PAGE_VALID | _PAGE_PRESENT)));
616ad3df56SEnze Li 	else
62*4574815aSHuacai Chen 		set_pte(pte, __pte(pte_val(ptep_get(pte)) | (_PAGE_VALID | _PAGE_PRESENT)));
636ad3df56SEnze Li 
646ad3df56SEnze Li 	preempt_disable();
656ad3df56SEnze Li 	local_flush_tlb_one(addr);
666ad3df56SEnze Li 	preempt_enable();
676ad3df56SEnze Li 
686ad3df56SEnze Li 	return true;
696ad3df56SEnze Li }
706ad3df56SEnze Li 
716ad3df56SEnze Li #endif /* _ASM_LOONGARCH_KFENCE_H */
72