我目前正在Aarch64体系结构上移植SO3操作系统。我使用QEMU/virt64仿真来实现这一点。只要我在内核空间运行EL1中的代码,一切都很顺利。但当我开始处理用户空间方面的问题时,我遇到了一些问题,并发现了各种各样的东西。我想确保我能正确地适应。
(SO3机制在非常简化的形式上与Linux非常相似。我们考虑48位VA寻址,ttbr0/ttbr1_el1目前使用相同的页表(。
首先,不可能在EL0模式下运行内核代码(因此代码位于地址>0xffff00.(,对吧?(目前,第一个用户线程prolog在内核空间开始执行,但在用户模式下,所以我需要在应用程序区域重新映射一些页面(或我错过了armv8支持的一些功能?
其次,我注意到在EL0中,任何使用位47访问VA的内存位置都会导致异常(在EL1中((我将堆栈顶部放在用户范围的最后几页中(知道它为什么失败了吗?(SCTRL/TCR的配置方式与Linux略有相同(
两种情况都会导致esr_el1:0x92000044(MMU故障(
ADDINGS
翻译有四个层次。
此外,奇怪的是:如果我在块描述符的TTE中设置了位6(AP1(,当MMU切换到L0页表时,它会失败。它只适用于页面TTE(级别3(。
以下是系统寄存器的值:
- TCR_EL1:0x15b5503510
- SCTLR_EL1:0x34f4d91d
TCR对应于:
#define TCR_CACHE_FLAGS TCR_IRGN_WBWA | TCR_ORGN_WBWA
#define TCR_TG_FLAGS TCR_TG0_4K | TCR_TG1_4K
#define TCR_SMP_FLAGS (TCR_SH0_INNER | TCR_SH1_INNER)
tcr = TCR_CACHE_FLAGS | TCR_SMP_FLAGS | TCR_TG_FLAGS | TCR_ASID16 | TCR_A1;
tcr |= TCR_TxSZ(48) | (TCR_PS_BITS_256TB << TCR_IPS_SHIFT);
和SCTLR到:
#define SCTLR_EL1_SET (SCTLR_ELx_M | SCTLR_ELx_C | SCTLR_ELx_SA |
SCTLR_EL1_SA0 | SCTLR_EL1_SED | SCTLR_ELx_I |
SCTLR_EL1_DZE | SCTLR_EL1_UCT |
SCTLR_EL1_NTWE | SCTLR_ELx_IESB | SCTLR_EL1_SPAN |
ENDIAN_SET_EL1 | SCTLR_EL1_UCI | SCTLR_EL1_RES1)
内存区域属性取自Linux:
#define MT_NORMAL 0
#define MT_NORMAL_TAGGED 1
#define MT_NORMAL_NC 2
#define MT_NORMAL_WT 3
#define MT_DEVICE_nGnRnE 4
#define MT_DEVICE_nGnRE 5
#define MT_DEVICE_GRE 6
/* MAIR_ELx memory attributes (used by Linux) */
#define MAIR_ATTR_DEVICE_nGnRnE UL(0x00)
#define MAIR_ATTR_DEVICE_nGnRE UL(0x04)
#define MAIR_ATTR_DEVICE_GRE UL(0x0c)
#define MAIR_ATTR_NORMAL_NC UL(0x44)
#define MAIR_ATTR_NORMAL_WT UL(0xbb)
#define MAIR_ATTR_NORMAL_TAGGED UL(0xf0)
#define MAIR_ATTR_NORMAL UL(0xff)
#define MAIR_ATTR_MASK UL(0xff)
/* Position the attr at the correct index */
#define MAIR_ATTRIDX(attr, idx) ((attr) << ((idx) * 8))
#define MAIR_EL1_SET
(MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRnE, MT_DEVICE_nGnRnE) |
MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRE, MT_DEVICE_nGnRE) |
MAIR_ATTRIDX(MAIR_ATTR_DEVICE_GRE, MT_DEVICE_GRE) |
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_NC, MT_NORMAL_NC) |
MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL) |
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_WT, MT_NORMAL_WT) |
MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL_TAGGED))
内核代码映射为MT_NORMAL
TTE配置有以下功能:
static inline void set_pte_table(u64 *pte, enum dcache_option option)
{
u64 attrs = PTE_TABLE_NS;
*pte |= PTE_TYPE_TABLE;
*pte |= attrs;
}
static inline void set_pte_block(u64 *pte, enum dcache_option option)
{
u64 attrs = PTE_BLOCK_MEMTYPE(option);
/* Set the PTE with R/W permissions for both kernel and user mode */
*pte |= PTE_TYPE_BLOCK | PTE_BLOCK_AF | PTE_BLOCK_INNER_SHARE | PTE_BLOCK_NS;
*pte |= attrs;
}
static inline void set_pte_page(u64 *pte, enum dcache_option option)
{
u64 attrs = PTE_BLOCK_MEMTYPE(option);
/* Set the PTE with R/W permissions for both kernel and user mode */
*pte |= PTE_TYPE_PAGE | PTE_BLOCK_AF | PTE_BLOCK_INNER_SHARE | PTE_BLOCK_NS | PTE_BLOCK_AP1;
*pte |= attrs;
}
如果我试图在set_PTE_BLOCK函数中设置PTE_BLOCK_AP1,它将失败。
适用于EL1和EL0的翻译机制是相同的。因此,如果您正确配置了内存系统,那么您绝对可以在内核模式下运行代码。你要确保:
TTE中的AP[1]
(比特6(是1
- 如果页面应可执行,则TTE中的
UXN
(位54(为0
- 没有一个页面表的
APTable
或UXNTable
位设置为不允许将页面映射为userland可访问/userland可执行的值 - 如果您使用的是ARMv8.1或更高版本,则PAN将被禁用。为此,请运行
msr pan, 0
并将SCTLR_EL1.SPAN
(位23(设置为1
(否则将在每个异常条目上重新启用PAN( - 如果您的目标具有
FEAT_E0PD
(在ARMv8.5中是必需的,在ARMv8.4中是可选的(,请确保TCR_EL1.E0PD1
(位56(是0
(假设您的内核位于地址空间的上半部分,否则是E0PD0
,位55(
(位从0开始编号,与ARMv8参考手册中的相同。(
对于您的第二个问题,我们需要知道您加载到TCR_EL1
中的确切值,但我的猜测是T0SZ
的值使TTBR0映射的地址空间小于48位。在任何一种情况下,当在异常向量处读取时,ESR_EL1
都将保持异常综合症。