ARM64 kernel中判定一个虚拟地址是否有对应的物理地址(是否可以访问)的方法
最近在做一个panic事后的处理时,需要读取寄存器信息,然后找到对应的物理地址。
然后发现Linux中,已经有实现的类似的功能,现在拿过来做地址有效性判定,感觉还是很不错的。
#include <asm/esr.h> #include <asm/sysreg.h> #include <asm/system_misc.h> static int check_addr_valid(unsigned long ptr) { unsigned long flags; unsigned long par; local_irq_save(flags); asm volatile("at s1e1r, %0" :: "r" (addr)); isb(); par = read_sysreg(par_el1); local_irq_restore(flags); if (par & SYS_PAR_EL1_F) return false; return true; }
具体可以看kernel实现:https://elixir.bootlin.com/linux/v5.4.108/source/arch/arm64/mm/fault.c#L254
后面还在 https://blog.csdn.net/weixin_42135087/article/details/106379970
看到optee中也有这样的方法,不仅能判定虚拟地址是否有效,还能根据虚拟地址得到物理地址的方法:
#include <linux/module.h> #define BIT32(nr) ((1 & 0xffffffff) << (nr)) #define BIT64(nr) ((1 & 0xffffffffffffffff) << (nr)) #define PAR_F BIT32(0) #define PAR_PA_SHIFT 12 #define PAR_PA_MASK (BIT64(36) - 1) #define DEFINE_REG_READ_FUNC_(reg, type, asmreg) \ static inline type read_##reg(void) \ { \ type val; \ \ asm volatile("mrs %0, " #asmreg : "=r" (val)); \ return val; \ } #define DEFINE_U64_REG_READ_FUNC(reg) \ DEFINE_REG_READ_FUNC_(reg, uint64_t, reg) DEFINE_U64_REG_READ_FUNC(par_el1) static inline void write_at_s1e1r(uint64_t va) { asm volatile ("at S1E1R, %0" : : "r" (va)); } bool arm_va2pa_helper(void *va, uint64_t *pa) { uint64_t par; uint64_t par_pa_mask; bool ret = false; local_irq_disable(); write_at_s1e1r((uint64_t)va); isb(); par = read_par_el1(); par_pa_mask = PAR_PA_MASK; if (par & PAR_F) goto out; *pa = (par & (par_pa_mask << PAR_PA_SHIFT)) | ((uint64_t)va & ((1 << PAR_PA_SHIFT) - 1)); ret = true; out: local_irq_enable();; return ret; } EXPORT_SYMBOL(arm_va2pa_helper);
这段代码在 https://elixir.bootlin.com/op-tee/latest/source/core/arch/arm/mm/core_mmu.c#L2048
可以看到,对应的32bit ARM CPU也有实现。