1、armv8 PAN指的是内核态不能访问用户态的数据,如果内核态想访问用户态的数据,需要copy_from_user,copy_to_user。
2、那么PAN是如何实现的呢?
通过CONFIG_ARM64_SW_TTBR0_PAN来配置是否开启PAN。
./arch/arm64/include/asm/uaccess.h
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
static inline void __uaccess_ttbr0_disable(void)
{
unsigned long flags, ttbr;
local_irq_save(flags);
ttbr = read_sysreg(ttbr1_el1);
ttbr &= ~TTBR_ASID_MASK;
/* reserved_ttbr0 placed at the end of swapper_pg_dir */
write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1); //ttbr_el1被置为无效,无法访问用户态的页表
isb();
/* Set reserved ASID */
write_sysreg(ttbr, ttbr1_el1);
isb();
local_irq_restore(flags);
}
static inline void __uaccess_ttbr0_enable(void)
{
unsigned long flags, ttbr0, ttbr1;
/*
* Disable interrupts to avoid preemption between reading the 'ttbr0'
* variable and the MSR. A context switch could trigger an ASID
* roll-over and an update of 'ttbr0'.
*/
local_irq_save(flags);
ttbr0 = READ_ONCE(current_thread_info()->ttbr0);//正确的用户态页表基地址保存在thread_info里面
/* Restore active ASID */
ttbr1 = read_sysreg(ttbr1_el1);
ttbr1 &= ~TTBR_ASID_MASK; /* safety measure */
ttbr1 |= ttbr0 & TTBR_ASID_MASK;
write_sysreg(ttbr1, ttbr1_el1);
isb();
/* Restore user page table */
write_sysreg(ttbr0, ttbr0_el1); //恢复正确的用户态页表基地址,可以访问用户态的页表
isb();
local_irq_restore(flags);
}
static inline bool uaccess_ttbr0_disable(void)
{
if (!system_uses_ttbr0_pan())
return false;
__uaccess_ttbr0_disable();
return true;
}
static inline bool uaccess_ttbr0_enable(void)
{
if (!system_uses_ttbr0_pan())
return false;
__uaccess_ttbr0_enable();
return true;
}
#else
static inline bool uaccess_ttbr0_disable(void)
{
return false;
}
static inline bool uaccess_ttbr0_enable(void)
{
return false;
}
#endif
uaccess_ttbr0_disable禁止内核态向用户态的访问,原理是将ttbr0_el1寄存器置为0,ttbr0_el1实际上保存的是一级页表的地址,所以ttbr0_el1被置零以后,内存页寻址失败,PAN生效。
uaccess_ttbr0_enable从thread_info中读取正确的用户态页表机制,然后恢复ttbr0_el1,这样就可以访问用户态的页表了,PAN失效。
3、什么时候调用这两个函数呢?
1)当用户态发生中断、异常、系统调用kernel_entry时;返回用户态kernel_exit时。
./arch/arm64/kernel/entry.S
kernel_entry:
....
alternative_if ARM64_HAS_PAN
b 1f // skip TTBR0 PAN
alternative_else_nop_endif
.if \el != 0
mrs x21, ttbr0_el1
tst x21, #TTBR_ASID_MASK // Check for the reserved ASID
orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR
b.eq 1f // TTBR0 access already disabled
and x23, x23, #~PSR_PAN_BIT // Clear the emulated PAN in the saved SPSR
.endif
__uaccess_ttbr0_disable x21
1:
....
此时el为0,一定会调用uaccess_ttbr0_disable来禁止访问用户态(PAN生效)。
kernel_exit:
....
alternative_if ARM64_HAS_PAN
b 2f // skip TTBR0 PAN
alternative_else_nop_endif
.if \el != 0
tbnz x22, #22, 1f // Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set
.endif
__uaccess_ttbr0_enable x0, x1
.if \el == 0
/*
* Enable errata workarounds only if returning to user. The only
* workaround currently required for TTBR0_EL1 changes are for the
* Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache
* corruption).
*/
bl post_ttbr_update_workaround
.endif
1:
....
此时el为0,一定会调用uaccess_ttbr0_enable来允许访问用户态(PAN失效)。因为已经返回了用户态,用户态当然可以访问用户态的页表了。
2)当内核态发生中断、系统调用kernel_entry时;返回内核态kernel_exit时。
如上面代码,kernel_entry,此时el为1,此时如果已经设置了PAN生效(用户态到内核态时设置的),则不会重新设置。且spsr的PAN标志位置1。
kernel_exit,此时el为1,如果spsr的PAN标志位为1,则不需要使PAN失效,因为此时处于内核态,PAN还应该生效。
3)copy_from_user、copy_to_user
ENTRY(__arch_copy_to_user)
uaccess_enable_not_uao x3, x4, x5
add end, x0, x2
#include "copy_template.S"
uaccess_disable_not_uao x3, x4
mov x0, #0
ret
ENDPROC(__arch_copy_to_user)
ENTRY(__arch_copy_from_user)
uaccess_enable_not_uao x3, x4, x5
add end, x0, x2
#include "copy_template.S"
uaccess_disable_not_uao x3, x4
mov x0, #0 // Nothing to copy
ret
ENDPROC(__arch_copy_from_user)
在和用户态交换数据时,先使PAN失效(可以访问用户态),再使PAN生效(不可以访问用户态)。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。