SylixOS 缺页异常
在arm64 中mmu访问错误会触发同步异常
在同步异常向量表中填入同步异常处理函数,同步异常函数会调用系统的archSyncExcHandle函数,可以看到此函数的第一个参数x0 是当前当前任务的TCB。x1 是ESR_EL1 的值,根据armv8手册此寄存器包含了一些异常的信息。
,特别是此寄存器中的EC 包含了同步错误的类型
根据不同类型的错误,采取不同的处理措施。
/*********************************************************************************************************
** 函数名称: archSyncExcHandle
** 功能描述: 处理 Sync 异常
** 输 入 : pregctx 上下文
** uiExcType 异常类型
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
VOID archSyncExcHandle (ARCH_REG_CTX *pregctx, UINT32 uiExcType)
{
#define ARM64_EXC_TYPE_UNKNOWN 10PLW_CLASS_TCB ptcbCur;LW_VMM_ABORT abtInfo;UINT uiExcClass;UINT uiExcISS;ULONG ulAbortAddr;LW_TCB_GET_CUR(ptcbCur);uiExcClass = (uiExcType >> 26) & 0x3f;uiExcISS = uiExcType & 0x1ffffff;ulAbortAddr = pregctx->REG_ulPC;switch (uiExcClass) {case EXC_UNKNOWN_REASON:case EXC_TRAP_WFI_WFE:case EXC_EL3:abtInfo.VMABT_uiMethod = 0;abtInfo.VMABT_uiType = ARM64_EXC_TYPE_UNKNOWN; /* 未知错误 */break;case EXC_TRAP_MCR_MRC_CO1111:case EXC_TRAP_MCRR_MRRC_CO1111:case EXC_TRAP_MCR_MRC_CO1110:case EXC_TRAP_LDC_STC:case EXC_TRAP_VMRS:case EXC_TRAP_MRRC_CO1110:case EXC_MSR_MRS_AARCH64:uiExcISS = uiExcType & 0x1;abtInfo.VMABT_uiMethod = uiExcISS ? LW_VMM_ABORT_METHOD_READ : LW_VMM_ABORT_METHOD_WRITE;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_PERM; /* 访问错误 */break;case EXC_ACCESS_SIMD_FP:case EXC_TRAP_FP_AARCH32:case EXC_TRAP_FP_AARCH64:abtInfo.VMABT_uiMethod = LW_VMM_ABORT_METHOD_EXEC;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_FPE; /* 浮点错误 */
#if LW_CFG_CPU_FPU_EN > 0if (archFpuUndHandle(ptcbCur) == ERROR_NONE) { /* 进行 FPU 指令探测 */return;}
#endif /* LW_CFG_CPU_FPU_EN > 0 */break;case EXC_ILLEGAL_EXEC:case EXC_PC_ALIGNMENT_FAULT:case EXC_SP_ALIGNMENT_FAULT:abtInfo.VMABT_uiMethod = LW_VMM_ABORT_METHOD_EXEC;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_BUS; /* 非对齐访问错误 */break;case EXC_SVC_AARCH32:case EXC_HVC_AARCH32:case EXC_SMC_AARCH32:case EXC_SVC_AARCH64:case EXC_HVC_AARCH64:case EXC_SMC_AARCH64:abtInfo.VMABT_uiMethod = LW_VMM_ABORT_METHOD_EXEC;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_SYS; /* 系统调用错误 */break;case EXC_INSTRUCTION_ABORT_LO:case EXC_INSTRUCTION_ABORT:abtInfo.VMABT_uiMethod = LW_VMM_ABORT_METHOD_EXEC;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_UNDEF; /* 指令错误 */break;case EXC_DATA_ABORT_LO:case EXC_DATA_ABORT:ulAbortAddr = arm64MmuAbtFaultAddr();abtInfo.VMABT_uiMethod = (uiExcISS & 0x40) ? LW_VMM_ABORT_METHOD_WRITE : LW_VMM_ABORT_METHOD_READ;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_MAP; /* 数据错误 */if (((uiExcISS & 0xf) == 0xf) && (abtInfo.VMABT_uiMethod == LW_VMM_ABORT_METHOD_WRITE)) {abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_PERM; /* 权限错误 */}break;case EXC_SERROR_INT:abtInfo.VMABT_uiMethod = 0;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_FATAL_ERROR; /* 致命错误 */break;case EXC_BREAKPOINT_LO:case EXC_BREAKPOINT:case EXC_SOFTWARE_STEP_LO:case EXC_SOFTWARE_STEP:case EXC_WATCHPOINT_LO:case EXC_WATCHPOINT:case EXC_BKPT_AARCH32:case EXC_VECTOR_CATCH_AARCH32:case EXC_BRK_AARCH64:abtInfo.VMABT_uiMethod = 0;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_BREAK; /* 断点异常 */#if LW_CFG_GDB_EN > 0if (API_DtraceBreakTrap(ulAbortAddr, LW_TRAP_BRKPT)== ERROR_NONE) { /* 进入调试接口断点处理 */return;}
#endif /* LW_CFG_GDB_EN > 0 */break;default:break;}#if LW_CFG_CPU_EXC_HOOK_EN > 0if (bspCpuExcHook(ptcbCur, pregctx->REG_ulPC, ulAbortAddr,abtInfo.VMABT_uiType, abtInfo.VMABT_uiMethod)) {return;}
#endifAPI_VmmAbortIsr(pregctx->REG_ulPC, ulAbortAddr, &abtInfo, ptcbCur);
}
以上是archSyncExcHandle函数,分析此函数
uiExcClass = (uiExcType >> 26) & 0x3f;uiExcISS = uiExcType & 0x1ffffff;ulAbortAddr = pregctx->REG_ulPC;
首先EC是从26位开始的所以当前,并且占用7位,所以当前uiExcClasss是单独将其取出来。当发生异常时arm寄存器已经被保存到了任务控制块tcb中这里获取PC就获取了地址。
错误的类型众多,我们只关心发生缺页中断时产生的错误
case EXC_DATA_ABORT_LO:case EXC_DATA_ABORT:ulAbortAddr = arm64MmuAbtFaultAddr();abtInfo.VMABT_uiMethod = (uiExcISS & 0x40) ? LW_VMM_ABORT_METHOD_WRITE : LW_VMM_ABORT_METHOD_READ;abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_MAP; /* 数据错误 */if (((uiExcISS & 0xf) == 0xf) && (abtInfo.VMABT_uiMethod == LW_VMM_ABORT_METHOD_WRITE)) {abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_PERM; /* 权限错误 */}break;
根据ESR_EL1寄存器的定义,这里的宏定义分别是
也就是手册中的
根据手册中的说明此错误是真多mmu 错误和堆栈指针未对齐等 。具体类型需要根据ESR_EL1寄存中的ISS位进行判断,根据不同的指令错误,ISS位的含义也不相同,具体可以参考armv8手册。
在发生错误的时候访问异常,需要知道访问的地址
ulAbortAddr = arm64MmuAbtFaultAddr();
;/*********************************************************************************************************
; MMU 异常地址获取
;*********************************************************************************************************/FUNC_DEF(arm64MmuAbtFaultAddr)MRS X0 , FAR_EL1RETFUNC_END()FILE_END()
使用FAR_EL1 寄存器获取异常地址。
此时异常地址和返回地址有已经获得,最后调用
API_VmmAbortIsr(pregctx->REG_ulPC, ulAbortAddr, &abtInfo, ptcbCur);
函数对当前的同步异常进行具体处理。
次函数使用上是下边函数的包装。
API_VmmAbortIsrEx(ulRetAddr, ulAbortAddr, pabtInfo, ptcb, __vmmAbortShell);
以下是 API_VmmAbortIsrEx函数。
/*********************************************************************************************************
** 函数名称: API_VmmAbortIsrEx
** 功能描述: 当 MMU 产生访问失效时, 调用此函数(类似于中断服务函数)
** 输 入 : ulRetAddr 异常返回地址
** ulAbortAddr 异常地址 (异常类型相关)
** pabtInfo 异常类型
** ptcb 出现异常的线程控制块 (不能为 NULL)
** pfuncHandler 异常处理函数
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
LW_API
VOID API_VmmAbortIsrEx (addr_t ulRetAddr, addr_t ulAbortAddr, PLW_VMM_ABORT pabtInfo, PLW_CLASS_TCB ptcb,VOIDFUNCPTR pfuncHandler)
{PLW_VMM_ABORT_CTX pabtctx;PLW_STACK pstkFailShell; /* 启动 fail shell 的堆栈点 */BYTE *pucStkNow; /* 记录还原堆栈点 */__vmmAbortFatalDetected(ulRetAddr, ulAbortAddr, pabtInfo, ptcb); /* 致命错误探测 */#if LW_CFG_VMM_EN > 0__vmmAbortStkOfDetected(ulRetAddr, ulAbortAddr, pabtInfo, ptcb); /* 是否堆栈溢出 */
#endif /* LW_CFG_VMM_EN > 0 */__KERNEL_ENTER(); /* 进入内核 *//* 产生异常 */pucStkNow = (BYTE *)archCtxStackEnd(&ptcb->TCB_archRegCtx); /* 记录还原堆栈点 */
#if CPU_STK_GROWTH == 0pucStkNow += sizeof(LW_STACK); /* 向空栈方向移动一个堆栈空间 */pucStkNow = (BYTE *)ROUND_UP(pucStkNow, ARCH_STK_ALIGN_SIZE);pabtctx = (PLW_VMM_ABORT_CTX)pucStkNow; /* 记录 PAGE_FAIL_CTX 位置 */pucStkNow += __ABTCTX_SIZE_ALIGN; /* 让出 PAGE_FAIL_CTX 空间 */
#elsepucStkNow -= __ABTCTX_SIZE_ALIGN; /* 让出 PAGE_FAIL_CTX 空间 */pucStkNow = (BYTE *)ROUND_DOWN(pucStkNow, ARCH_STK_ALIGN_SIZE);pabtctx = (PLW_VMM_ABORT_CTX)pucStkNow; /* 记录 PAGE_FAIL_CTX 位置 */pucStkNow -= sizeof(LW_STACK); /* 向空栈方向移动一个堆栈空间 */
#endifpabtctx->ABTCTX_ptcb = ptcb;pabtctx->ABTCTX_ulRetAddr = ulRetAddr; /* 异常返回地址 */pabtctx->ABTCTX_ulAbortAddr = ulAbortAddr; /* 异常地址 (异常类型相关) */pabtctx->ABTCTX_abtInfo = *pabtInfo; /* 异常类型 */pabtctx->ABTCTX_archRegCtx = ptcb->TCB_archRegCtx;pabtctx->ABTCTX_iLastErrno = (errno_t)ptcb->TCB_ulLastError;pabtctx->ABTCTX_iKernelSpace = __KERNEL_SPACE_GET2(ptcb);pstkFailShell = archTaskCtxCreate(&ptcb->TCB_archRegCtx,(PTHREAD_START_ROUTINE)pfuncHandler,(PVOID)pabtctx,(PLW_STACK)pucStkNow,0); /* 建立访问异常陷阱外壳环境 */archTaskCtxSetFp(pstkFailShell,&ptcb->TCB_archRegCtx,&pabtctx->ABTCTX_archRegCtx); /* 保存 fp, 使 callstack 正常 */_StackCheckGuard(ptcb); /* 堆栈警戒检查 */__KERNEL_EXIT(); /* 退出内核 */#if LW_CFG_CPU_FPU_EN > 0if (__ABTCTX_ABORT_TYPE(pabtctx) == LW_VMM_ABORT_TYPE_FPE) {if (ptcb->TCB_ulOption & LW_OPTION_THREAD_USED_FP) { /* 如果为 FPU 异常 */__ARCH_FPU_SAVE(ptcb->TCB_pvStackFP); /* 需要保存当前 FPU CTX */}}
#endif /* LW_CFG_CPU_FPU_EN > 0 */#if LW_CFG_CPU_DSP_EN > 0if (__ABTCTX_ABORT_TYPE(pabtctx) == LW_VMM_ABORT_TYPE_DSPE) {if (ptcb->TCB_ulOption & LW_OPTION_THREAD_USED_DSP) { /* 如果为 DSP 异常 */__ARCH_DSP_SAVE(ptcb->TCB_pvStackDSP); /* 需要保存当前 DSP CTX */}}
#endif /* LW_CFG_CPU_DSP_EN > 0 */
}
首先获取了当前的任务的堆栈指针
pucStkNow = (BYTE *)archCtxStackEnd(&ptcb->TCB_archRegCtx); /* 记录还原堆栈点 */
这里获取堆栈指针是要下边创建线程陷阱。
根据是低地址往高地址还是高地址往低地址,目前预留出异常返回结构体的空间
#if CPU_STK_GROWTH == 0pucStkNow += sizeof(LW_STACK); /* 向空栈方向移动一个堆栈空间 */pucStkNow = (BYTE *)ROUND_UP(pucStkNow, ARCH_STK_ALIGN_SIZE);pabtctx = (PLW_VMM_ABORT_CTX)pucStkNow; /* 记录 PAGE_FAIL_CTX 位置 */pucStkNow += __ABTCTX_SIZE_ALIGN; /* 让出 PAGE_FAIL_CTX 空间 */
#elsepucStkNow -= __ABTCTX_SIZE_ALIGN; /* 让出 PAGE_FAIL_CTX 空间 */pucStkNow = (BYTE *)ROUND_DOWN(pucStkNow, ARCH_STK_ALIGN_SIZE);pabtctx = (PLW_VMM_ABORT_CTX)pucStkNow; /* 记录 PAGE_FAIL_CTX 位置 */pucStkNow -= sizeof(LW_STACK); /* 向空栈方向移动一个堆栈空间 */
#endif
然后对异常返回空间的值进行赋值
pabtctx->ABTCTX_ptcb = ptcb;pabtctx->ABTCTX_ulRetAddr = ulRetAddr; /* 异常返回地址 */pabtctx->ABTCTX_ulAbortAddr = ulAbortAddr; /* 异常地址 (异常类型相关) */pabtctx->ABTCTX_abtInfo = *pabtInfo; /* 异常类型 */pabtctx->ABTCTX_archRegCtx = ptcb->TCB_archRegCtx;pabtctx->ABTCTX_iLastErrno = (errno_t)ptcb->TCB_ulLastError;pabtctx->ABTCTX_iKernelSpace = __KERNEL_SPACE_GET2(ptcb);
然后创建任务
/*********************************************************************************************************
** 函数名称: archTaskCtxCreate
** 功能描述: 创建任务上下文
** 输 入 : pregctx 寄存器上下文
** pfuncTask 任务入口
** pvArg 入口参数
** pstkTop 初始化堆栈起点
** ulOpt 任务创建选项
** 输 出 : 初始化堆栈结束点
** 全局变量:
** 调用模块:
** 注 意 : 堆栈从高地址向低地址增长.
*********************************************************************************************************/
PLW_STACK archTaskCtxCreate (ARCH_REG_CTX *pregctx,PTHREAD_START_ROUTINE pfuncTask,PVOID pvArg,PLW_STACK pstkTop, ULONG ulOpt)
{ARCH_FP_CTX *pfpctx;ARCH_REG_T ulPstate;INT i;pstkTop = (PLW_STACK)ROUND_DOWN(pstkTop, ARCH_STK_ALIGN_SIZE); /* 堆栈指针向下 16 字节对齐 */pfpctx = (ARCH_FP_CTX *)((PCHAR)pstkTop - sizeof(ARCH_FP_CTX));pfpctx->FP_ulFp = (ARCH_REG_T)LW_NULL;pfpctx->FP_ulLr = (ARCH_REG_T)LW_NULL;ulPstate = arm64GetNZCV() | /* 获得当前 NZCV 寄存器 */arm64GetDAIF(); /* 获得当前 DAIF 寄存器 */ulPstate &= ~M_PSTATE_I; /* 使能 IRQ */pregctx->REG_ulPstate = ulPstate;/** 初始化寄存器上下文*/for (i = 0; i < ARCH_GREG_NR; i++) {pregctx->REG_ulReg[i] = i;}pregctx->REG_ulSmallCtx = 1; /* 小上下文 */pregctx->REG_ulReg[0] = (ARCH_REG_T)pvArg;pregctx->REG_ulLR = (ARCH_REG_T)pfuncTask;pregctx->REG_ulPC = (ARCH_REG_T)pfuncTask;pregctx->REG_ulSP = (ARCH_REG_T)pfpctx;return ((PLW_STACK)pfpctx);
}
上面函数是在arm64 创建任务上下文,首先第一个参数将TCB控制块的上下文控制块,分配一个栈帧的空间,好执行完毕后返回,函授获取状态寄存器,给TCB中上下文控制块中的寄状态寄存器赋值,然后将参数赋值给Reg[0],需要执行的任务函数地址赋值给LR和PC。sp赋值单签的栈帧地址。
archTaskCtxSetFp(pstkFailShell,&ptcb->TCB_archRegCtx,&pabtctx->ABTCTX_archRegCtx); /* 保存 fp, 使 callstack 正常 */
最后执行此函数就是将栈帧地址赋值给TCB上下文控制块中的栈帧变量。等待退出函数时tcb上下文控制块寄存器填写到arm寄存器中,然后开始执行__vmmAbortShell 任务。
/*********************************************************************************************************
** 函数名称: __vmmAbortShell
** 功能描述: 当 MMU 产生访问失效时, 线程执行陷阱函数.
** 输 入 : pabtctx page fail 上下文
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID __vmmAbortShell (PLW_VMM_ABORT_CTX pabtctx)
{INTREG iregInterLevel;addr_t ulAbortAddr = pabtctx->ABTCTX_ulAbortAddr;ULONG ulFlag;REGISTER PLW_VMM_PAGE pvmpageVirtual;REGISTER PLW_VMM_PAGE pvmpagePhysical;REGISTER PLW_VMM_PAGE_PRIVATE pvmpagep;ULONG ulAllocPageNum;BOOL bSwapNeedLoad;addr_t ulVirtualPageAlign;ULONG ulError;INT iRet;PLW_CLASS_TCB ptcbCur; /* 当前任务控制块 */if (__KERNEL_ISENTER()) {__vmmAbortDump(pabtctx); /* 打印关键信息 */__vmmAbortKill(pabtctx);goto __abort_return;}LW_TCB_GET_CUR_SAFE(ptcbCur);__VMM_LOCK();_K_vmmStatus.VMMS_i64AbortCounter++;if ((__ABTCTX_ABORT_TYPE(pabtctx) != LW_VMM_ABORT_TYPE_MAP) &&(__ABTCTX_ABORT_TYPE(pabtctx) != LW_VMM_ABORT_TYPE_PERM)) {__VMM_UNLOCK();__vmmAbortAccess(pabtctx); /* 访问异常情况 */goto __abort_return; /* 不会运行到这里 */}pvmpageVirtual = __vmmAbortPageGet(ulAbortAddr); /* 获得对应虚拟内存控制块 */if (pvmpageVirtual) {pvmpagep = (PLW_VMM_PAGE_PRIVATE)pvmpageVirtual->PAGE_pvAreaCb;} else {__VMM_UNLOCK();__vmmAbortAccess(pabtctx); /* 访问异常 */goto __abort_return; /* 不会运行到这里 */}_K_vmmStatus.VMMS_i64PageFailCounter++; /* 缺页中断次数++ */ptcbCur->TCB_i64PageFailCounter++; /* 缺页中断次数++ */ulVirtualPageAlign = ulAbortAddr & LW_CFG_VMM_PAGE_MASK; /* 获得访问地址页边界 */ulError = __vmmLibGetFlag(ulVirtualPageAlign, &ulFlag); /* 获得异常地址的物理页面属性 */if (ulError == ERROR_NONE) { /* 存在物理页面正常 */if (__ABTCTX_ABORT_TYPE(pabtctx) == LW_VMM_ABORT_TYPE_MAP) { /* MAP 类型错误 */__VMM_UNLOCK();goto __abort_return; /* 页表已经正常, 可以访问 */}/* 写入异常 */if (__ABTCTX_ABORT_METHOD(pabtctx) == LW_VMM_ABORT_METHOD_WRITE) {if (__vmmAbortWriteProtect(pvmpageVirtual, /* 尝试 copy-on-write 处理 */ulVirtualPageAlign,ulError) == ERROR_NONE) { /* 进入写保护处理 */__VMM_UNLOCK();goto __abort_return;}}__VMM_UNLOCK();__vmmAbortAccess(pabtctx); /* 非法内存访问 */goto __abort_return; /* 不会运行到这里 */} else { /* 映射错误, 没有物理页面存在 */if (pvmpagep->PAGEP_iFlags & LW_VMM_SHARED_CHANGE) { /* 共享区间 */if (__vmmAbortShare(pvmpageVirtual, pvmpagep,ulVirtualPageAlign) == ERROR_NONE) { /* 尝试共享 */__VMM_UNLOCK();goto __abort_return;}}ulAllocPageNum = __PAGEFAIL_ALLOC_PAGE_NUM; /* 缺页中断分配的内存页面个数 */pvmpagePhysical = __vmmAbortNewPage(ulAllocPageNum); /* 分配物理页面 */if (pvmpagePhysical == LW_NULL) {__VMM_UNLOCK();__ABTCTX_ABORT_TYPE(pabtctx) = LW_VMM_ABORT_TYPE_NOINFO; /* 缺少物理页面 */printk(KERN_CRIT "kernel no more physical page.\n"); /* 系统无法分配物理页面 */__vmmAbortKill(pabtctx);goto __abort_return;}bSwapNeedLoad = __vmmPageSwapIsNeedLoad(__PAGEFAIL_CUR_PID,ulVirtualPageAlign); /* 检查是否需要 load swap 数据 */if (bSwapNeedLoad) {iRet = __vmmAbortSwapPage(pvmpagePhysical, ulVirtualPageAlign,ulAllocPageNum); /* 进行页面交换处理 */} else {iRet = __vmmAbortNoPage(pvmpagePhysical, pvmpagep,ulVirtualPageAlign, ulAllocPageNum); /* 进行页面填充处理 */}if (iRet != ERROR_NONE) {__vmmPhysicalPageFree(pvmpagePhysical);__VMM_UNLOCK();__vmmAbortKill(pabtctx);goto __abort_return;}}ulError = __vmmLibPageMap(pvmpagePhysical->PAGE_ulPageAddr, /* 与分配时相同的属性 */ulVirtualPageAlign,ulAllocPageNum, pvmpageVirtual->PAGE_ulFlags); /* 映射指定的虚拟地址 */if (ulError) {_K_vmmStatus.VMMS_i64MapErrCounter++;__vmmPhysicalPageFree(pvmpagePhysical);__VMM_UNLOCK();printk(KERN_CRIT "kernel physical page map error.\n"); /* 系统无法映射物理页面 */__vmmAbortKill(pabtctx);goto __abort_return;}pvmpagePhysical->PAGE_ulMapPageAddr = ulVirtualPageAlign;pvmpagePhysical->PAGE_ulFlags = pvmpageVirtual->PAGE_ulFlags;__pageLink(pvmpageVirtual, pvmpagePhysical); /* 建立连接关系 */__VMM_UNLOCK();__abort_return:__KERNEL_SPACE_SET(pabtctx->ABTCTX_iKernelSpace); /* 恢复成进入之前的状态 */errno = pabtctx->ABTCTX_iLastErrno; /* 恢复之前的 errno */iregInterLevel = KN_INT_DISABLE(); /* 关闭当前 CPU 中断 */KN_SMP_MB();archSigCtxLoad(&pabtctx->ABTCTX_archRegCtx); /* 从 page fail 上下文中返回 */KN_INT_ENABLE(iregInterLevel); /* 运行不到这里 */
}
首先获取发生异常页面的属性
ULONG __vmmLibGetFlag (addr_t ulVirtualAddr, ULONG *pulFlag)
{PLW_MMU_CONTEXT pmmuctx = __vmmGetCurCtx();ULONG ulFlag;ulFlag = __VMM_MMU_FLAG_GET(pmmuctx, ulVirtualAddr);if (pulFlag) {*pulFlag = ulFlag;}if (ulFlag & LW_VMM_FLAG_VALID) {return (ERROR_NONE);} else {_ErrorHandle(ERROR_VMM_PAGE_INVAL);return (ERROR_VMM_PAGE_INVAL);}
}
此函数会查证映射是否有效,其实是调用的mmu函数的的属性get函数,在arm64 中对应
static ULONG arm64MmuFlagGet (PLW_MMU_CONTEXT pmmuctx, addr_t ulAddr)
{LW_PGD_TRANSENTRY *p_pgdentry = arm64MmuPgdOffset(pmmuctx, ulAddr);/* 获取一级描述符 */INT iDescType;ULONG ulFlag = 0;UINT8 ucGuard; /* 严格的权限检查 */UINT8 ucXN; /* 可执行权限标志 */UINT8 ucPXN; /* 特权可执行权限标志 */UINT8 ucCon; /* Contiguous 标志 */UINT8 ucnG; /* nG 标志 */UINT8 ucAF; /* 是否拥有访问权限标志 */UINT8 ucSH; /* 共享权限标志 */UINT8 ucAP; /* 是否可写权限标志 */UINT8 ucNS; /* Non-Secure 标志 */UINT8 ucAIn;iDescType = (*p_pgdentry) & 0x03; /* 获得一级页表类型 */if (iDescType == ARM64_PGD_TYPE_BLOCK) { /* 基于段的映射 */return (LW_VMM_FLAG_UNVALID);} else if (iDescType == ARM64_PGD_TYPE_TABLE) { /* 基于四级页表映射 */LW_PMD_TRANSENTRY *p_pmdentry = arm64MmuPmdOffset((LW_PMD_TRANSENTRY *)p_pgdentry,ulAddr); /* 获取二级描述符 */if (arm64MmuPmdIsOk(*p_pmdentry)) {LW_PTS_TRANSENTRY *p_ptsentry = arm64MmuPtsOffset((LW_PTS_TRANSENTRY *)p_pmdentry,ulAddr);/* 获取三级描述符 */if (arm64MmuPtsIsOk(*p_ptsentry)) { LW_PTE_TRANSENTRY *p_pteentry = arm64MmuPteOffset((LW_PTE_TRANSENTRY *)p_ptsentry,ulAddr);/* 获取四级描述符 */if (arm64MmuPteIsOk(*p_pteentry)) {UINT64 u64Descriptor = (UINT64)(*p_pteentry);ucGuard = (UINT8)((u64Descriptor & ARM64_PTE_GUARD_MASK) >> ARM64_PTE_GUARD_SHIFT);ucXN = (UINT8)((u64Descriptor & ARM64_PTE_UXN_MASK) >> ARM64_PTE_UXN_SHIFT);ucPXN = (UINT8)((u64Descriptor & ARM64_PTE_PXN_MASK) >> ARM64_PTE_PXN_SHIFT);ucCon = (UINT8)((u64Descriptor & ARM64_PTE_CONT_MASK) >> ARM64_PTE_CONT_SHIFT);ucnG = (UINT8)((u64Descriptor & ARM64_PTE_NG_MASK) >> ARM64_PTE_NG_SHIFT);ucAF = (UINT8)((u64Descriptor & ARM64_PTE_AF_MASK) >> ARM64_PTE_AF_SHIFT);ucSH = (UINT8)((u64Descriptor & ARM64_PTE_SH_MASK) >> ARM64_PTE_SH_SHIFT);ucAP = (UINT8)((u64Descriptor & ARM64_PTE_AP_MASK) >> ARM64_PTE_AP_SHIFT);ucNS = (UINT8)((u64Descriptor & ARM64_PTE_NS_MASK) >> ARM64_PTE_NS_SHIFT);ucAIn = (UINT8)((u64Descriptor & ARM64_PTE_AIN_MASK) >> ARM64_PTE_AIN_SHIFT);arm64MmuAttr2Flags(ucGuard, ucXN, ucPXN, ucCon, ucnG,ucAF, ucSH, ucAP, ucNS, ucAIn, &ulFlag);return (ulFlag);}}}}return (LW_VMM_FLAG_UNVALID);
}
可以看出,当pmd或者pts,pts中不存在此虚拟地址时返回无映射,此时在__vmmAbortShell函数中走else分支
首选是检查是否有共享页面。 共享页面是mmap 函数中用到。这里先不讨论共享页面。
不需要共享页面时,首先分配物理页面,然后将物理页面和虚拟页面建立映射。
在最后页面映射完成后,需要重新返回发生缺页中断的线程调用加载函数
在之前创建陷阱时已经将进程的上下文信息保存在pabtctx中,所以此时将上下文恢复到寄存器中,继续原来程序执行。
SylixOS 缺页异常相关推荐
- 模拟请求分页管理中地址转换和缺页中断处理_Linux内存管理:缺页异常(一)
缺页异常: 缺页异常(Page Faults)属于ARM V8处理器的异常类型中的同步异常.当MMU走表时可能会产生若干种类型的MMU faults(有同步的也有异步的),其中的同步异常,即这里将要讨 ...
- (41)缺页异常简介
一.什么情况下会引起缺页异常 很简单,PTE的P=0时,就会引发缺页异常. 缺页异常有4种情形,见下图: 不常使用的线性地址可能是没有挂物理页的,当我们访问这个地址,由于PTE P=0,会触发缺页异常 ...
- Windows内存管理学习笔记(三)—— 无处不在的缺页异常
Windows内存管理学习笔记(三)-- 无处不在的缺页异常 缺页异常 实验一:设置虚拟内存 无处不在的缺页 位于页面文件 保留与提交的误区 实验二:理解缺页异常 EXECUTE_WRITECOPY ...
- mmap文件映射与缺页异常 | 图
缺页异常
- linux内存管理(九)-缺页异常分析
缺页异常被触发通常有两种情况 a.程序设计的不当导致访问了非法的地址 b.访问的地址是合法的,但是该地址还未分配物理页框 下面解释一下第二种情况,这是虚拟内存管理的一个特性.尽管每个进程独立拥有3GB ...
- linux缺页异常,操作系统缺页异常
缺页异常,页缺失 Page fault,指的是硬错误.硬中断.分页错误.寻页缺失.缺页中断.页故障等)指的是当软件试图访问已映射在虚拟地址空间中,但是目前并未被加载在物理内存中的一个分页时,由中央处理 ...
- 6.S081-6缺页异常 - lazy allocation - Page Fault
6.S081-6缺页异常Page Fault 这一节课,可以帮我们完成2个实验: 题目要求链接:Lab: xv6 lazy page allocation 对应做法链接:6.S081 Lab4 Laz ...
- 一文透彻了解缺页异常
缺页异常处理流程图解 首先明确下什么是缺页异常,CPU通过地址总线可以访问连接在地址总线上的所有外设,包括物理内存.IO设备等等,但从CPU发出的访问地址并非是这些外设在地址总线上的物理地址,而是一个 ...
- 页错误 Page Fault /缺页异常 详解
目录 1. 第一部分:如果你看得懂 1.1 页错误定义 1.2 页错误的处理 2. 第二部分:如果你看不懂上面的,请看这里 2.1. 举例子(背景) 2.1.1 进程及页映射 ...
- Linux内核情景分析之异常访问,用户堆栈的扩展
情景假设: 在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存.也就是所说的野指针访问. 当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况 1.如果 ...
最新文章
- 某程序员吐槽:媳妇要给孩子报少儿编程班,将来继续做程序员!以后要看到穿着纸尿裤的P7!...
- 7 papers | NeurIPS 2019获奖论文;OpenAI刀塔2论文公布
- CSP认证201409-2	画图[C++题解]:模拟、图形的并
- python代码有时候在命令行下和Python Shell中执行的结果不一样?
- PHP如何处理emoji表情存入utf8的数据库
- PCL的学习必要性、重要性、意义及最初——持续修改中
- python如何安装wordcloud_不知如何利用Python中wordcloud的安装和使用?这里手把手教你...
- 牛客题霸 [容器盛水问题] C++题解/答案
- 华尔街宫斗戏升温:银行巨头和纽交所争夺交易数据所有权
- 安卓更改目标sdk_您最近是否修改了目标?
- 3d数学基础:图形和游戏开发(第2版)_人教版五年级数学上册教学计划进度表
- Linux之远程连接服务器ssh、telnet
- 计算机视觉领域的一些牛人博客,超有实力的研究机构等的网站链接---个人整理
- 142个手机短信笑话
- 如何度过有用的每一天
- 在python培训价格
- HarmonyOS——一个面向物联网的操作系统
- C语言 | 常见问题汇总
- Android利用Cookie实现码源登录效果二
- 基于html的购物网站【华为手机购物网站制作】学生网页设计作业源码