前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >armv8/arm64 PAN深入分析

armv8/arm64 PAN深入分析

原创
作者头像
jltxgcy
发布2019-04-14 13:41:47
3.9K0
发布2019-04-14 13:41:47
举报
文章被收录于专栏:arm内核arm内核arm内核

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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档