zImage
从Hyper态返回SVC态
//reg—暂存寄存器
.macro safe_svcmode_maskall reg:req
#if __LINUX_ARM_ARCH__ >= 6
//读取cpsr到暂存寄存器reg
mrs
\reg , cpsr
/*以下两条指令区分当前cpsr是否处在HYP_MODE,若处在HYP_MODE模式,标志位置零*/
eor
\reg, \reg, #HYP_MODE
tst
\reg, #MODE_MASK
bic
\reg , \reg , #MODE_MASK
//在暂存寄存器里存放SVC控制位
orr
\reg , \reg , #PSR_I_BIT | PSR_F_BIT | SVC_MODE
THUMB(
orr
\reg , \reg , #PSR_T_BIT
)
/*若当前寄存器处于hyper模式,返回SVC模式走一个杜撰的HVC异常返回。*/
bne
1f
orr
\reg, \reg, #PSR_A_BIT
/*设置返回到SVC态的地址是标号2,即该宏调用的后一条指令。*/
adr
lr, BSYM(2f)
//退出hyper后的cpsr寄存器
msr
spsr_cxsf, \reg
//放到ELR_hyp中
__MSR_ELR_HYP(14)
//返回到SVC态
__ERET
//当前处理器不在hyper状态,强制切换到SVC态
1:
msr
cpsr_c, \reg
2:
Uboot在arm hyper状态引导因zImage。zImage除了需要支持传统的解压、定位工作,还要处理好hyper的状态维护工作。这里的工作分为三个部分
在hyper状态下设置zImage特有的hyper异常表“__hyp_stub_vectors”,
在SVC状态下完成zImage的解压定位工作。
通过hyper异常表“__hyp_stub_vectors”的hyper异常,设置新的hyper异常表:“__hyp_reentry_vectors”并由此进入kernel。
zImage在获得控制权之后第一件事要设置hyper状态的异常表,并返回SVC态,这通过“__hyp_stub_install”来完成:
ENTRY(__hyp_stub_install)
…
adr
r7, __hyp_stub_vectors
mcr
p15, 4, r7, c12, c0, 0
@ set hypervisor vector base (HVBAR)
zImage使用的第一个hyper异常表,在工作时守护在hyper态:
__hyp_stub_vectors:
…
__hyp_stub_trap:
W(b)
__hyp_stub_do_trap
…
可见,该异常表为zIamge提供了一个hyper模式入口:
//该函数为SVC模式提供两种选择,读取HVBAR或设置HVBAR
__hyp_stub_do_trap:
//检查功能选择,若设置HVBAR,r0携带者HVBAR地址
cmp
r0, #-1
//读取HVBAR
mrceq
p15, 4, r0, c12, c0, 0
@ get HVBAR
//设置HVBAR
mcrne
p15, 4, r0, c12, c0, 0
@ set HVBAR
//返回到SVC状态
__ERET
ENDPROC(__hyp_stub_do_trap)
若zImage若需改动HVBAR可通过如下函数进行
//读HVBAR
ENTRY(__hyp_get_vectors)
//r0携带-1
mov
r0, #-1
//设置HVBAR,这之前r0已经赋予异常表地址
ENDPROC(__hyp_get_vectors)
ENTRY(__hyp_set_vectors)
//调用hyper异常
__HVC(0)
mov
pc, lr
head.s
kernel
static void cpu_init_hyp_mode(void *dummy)
{
…
//设置hyper态的新的异常表:“__kvm_hyp_init”
__hyp_set_vectors(kvm_get_idmap_vector());
boot_pgd_ptr = kvm_mmu_get_boot_httbr();
pgd_ptr = kvm_mmu_get_httbr();
stack_page = __this_cpu_read(kvm_arm_hyp_stack_page);
hyp_stack_ptr = stack_page + PAGE_SIZE;
vector_ptr = (unsigned long)__kvm_hyp_vector;
//hvc调用“__do_hyp_init”
__cpu_init_hyp_mode(boot_pgd_ptr, pgd_ptr, hyp_stack_ptr, vector_ptr);
}
static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,
phys_addr_t pgd_ptr,
unsigned long hyp_stack_ptr,
unsigned long vector_ptr)
{
kvm_call_hyp(NULL, 0, boot_pgd_ptr);
kvm_call_hyp((void*)hyp_stack_ptr, vector_ptr, pgd_ptr);
}
Hyper态的页表页目录
int kvm_mmu_init(void)
{
int err;
/* hyp_idmap_start 与hyp_idmap_end 是“kvm/init.S”起始物理地址“PA”*/
hyp_idmap_start = kvm_virt_to_phys(__hyp_idmap_text_start);
hyp_idmap_end = kvm_virt_to_phys(__hyp_idmap_text_end);
hyp_idmap_vector = kvm_virt_to_phys(__kvm_hyp_init);
…
// hyp_pgd,hyper态的主页表页目录
hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
//hyper初始化代码“kvm/init.S”使用的页表页目录
boot_hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
…
/*在boot_hyp_pgd映射“kvm/init.S”所在物理地址 */
err =
__create_hyp_mappings(boot_hyp_pgd,
hyp_idmap_start, hyp_idmap_end,
__phys_to_pfn(hyp_idmap_start),
PAGE_HYP);
…
/* 在boot_hyp_pgd映射“kvm/init.S”所在物理地址 */
err =
__create_hyp_mappings(boot_hyp_pgd,
TRAMPOLINE_VA, TRAMPOLINE_VA + PAGE_SIZE,
__phys_to_pfn(hyp_idmap_start),
PAGE_HYP);
…
/* Map the same page again into the runtime page tables */
err =
__create_hyp_mappings(hyp_pgd,
TRAMPOLINE_VA, TRAMPOLINE_VA + PAGE_SIZE,
__phys_to_pfn(hyp_idmap_start),
PAGE_HYP);
…
return 0;
out:
free_hyp_pgds();
return err;
}