当前位置:   article > 正文

SylixOS 缺页异常_vmmabortfataldetected

vmmabortfataldetected

在arm64 中mmu访问错误会触发同步异常

在同步异常向量表中填入同步异常处理函数,同步异常函数会调用系统的archSyncExcHandle函数,可以看到此函数的第一个参数x0 是当前当前任务的TCB。x1 是ESR_EL1 的值,根据armv8手册此寄存器包含了一些异常的信息。

,特别是此寄存器中的EC 包含了同步错误的类型

根据不同类型的错误,采取不同的处理措施。

  1. /*********************************************************************************************************
  2. ** 函数名称: archSyncExcHandle
  3. ** 功能描述: 处理 Sync 异常
  4. ** 输 入 : pregctx 上下文
  5. ** uiExcType 异常类型
  6. ** 输 出 : NONE
  7. ** 全局变量:
  8. ** 调用模块:
  9. *********************************************************************************************************/
  10. VOID archSyncExcHandle (ARCH_REG_CTX *pregctx, UINT32 uiExcType)
  11. {
  12. #define ARM64_EXC_TYPE_UNKNOWN 10
  13. PLW_CLASS_TCB ptcbCur;
  14. LW_VMM_ABORT abtInfo;
  15. UINT uiExcClass;
  16. UINT uiExcISS;
  17. ULONG ulAbortAddr;
  18. LW_TCB_GET_CUR(ptcbCur);
  19. uiExcClass = (uiExcType >> 26) & 0x3f;
  20. uiExcISS = uiExcType & 0x1ffffff;
  21. ulAbortAddr = pregctx->REG_ulPC;
  22. switch (uiExcClass) {
  23. case EXC_UNKNOWN_REASON:
  24. case EXC_TRAP_WFI_WFE:
  25. case EXC_EL3:
  26. abtInfo.VMABT_uiMethod = 0;
  27. abtInfo.VMABT_uiType = ARM64_EXC_TYPE_UNKNOWN; /* 未知错误 */
  28. break;
  29. case EXC_TRAP_MCR_MRC_CO1111:
  30. case EXC_TRAP_MCRR_MRRC_CO1111:
  31. case EXC_TRAP_MCR_MRC_CO1110:
  32. case EXC_TRAP_LDC_STC:
  33. case EXC_TRAP_VMRS:
  34. case EXC_TRAP_MRRC_CO1110:
  35. case EXC_MSR_MRS_AARCH64:
  36. uiExcISS = uiExcType & 0x1;
  37. abtInfo.VMABT_uiMethod = uiExcISS ? LW_VMM_ABORT_METHOD_READ : LW_VMM_ABORT_METHOD_WRITE;
  38. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_PERM; /* 访问错误 */
  39. break;
  40. case EXC_ACCESS_SIMD_FP:
  41. case EXC_TRAP_FP_AARCH32:
  42. case EXC_TRAP_FP_AARCH64:
  43. abtInfo.VMABT_uiMethod = LW_VMM_ABORT_METHOD_EXEC;
  44. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_FPE; /* 浮点错误 */
  45. #if LW_CFG_CPU_FPU_EN > 0
  46. if (archFpuUndHandle(ptcbCur) == ERROR_NONE) { /* 进行 FPU 指令探测 */
  47. return;
  48. }
  49. #endif /* LW_CFG_CPU_FPU_EN > 0 */
  50. break;
  51. case EXC_ILLEGAL_EXEC:
  52. case EXC_PC_ALIGNMENT_FAULT:
  53. case EXC_SP_ALIGNMENT_FAULT:
  54. abtInfo.VMABT_uiMethod = LW_VMM_ABORT_METHOD_EXEC;
  55. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_BUS; /* 非对齐访问错误 */
  56. break;
  57. case EXC_SVC_AARCH32:
  58. case EXC_HVC_AARCH32:
  59. case EXC_SMC_AARCH32:
  60. case EXC_SVC_AARCH64:
  61. case EXC_HVC_AARCH64:
  62. case EXC_SMC_AARCH64:
  63. abtInfo.VMABT_uiMethod = LW_VMM_ABORT_METHOD_EXEC;
  64. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_SYS; /* 系统调用错误 */
  65. break;
  66. case EXC_INSTRUCTION_ABORT_LO:
  67. case EXC_INSTRUCTION_ABORT:
  68. abtInfo.VMABT_uiMethod = LW_VMM_ABORT_METHOD_EXEC;
  69. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_UNDEF; /* 指令错误 */
  70. break;
  71. case EXC_DATA_ABORT_LO:
  72. case EXC_DATA_ABORT:
  73. ulAbortAddr = arm64MmuAbtFaultAddr();
  74. abtInfo.VMABT_uiMethod = (uiExcISS & 0x40) ? LW_VMM_ABORT_METHOD_WRITE : LW_VMM_ABORT_METHOD_READ;
  75. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_MAP; /* 数据错误 */
  76. if (((uiExcISS & 0xf) == 0xf) && (abtInfo.VMABT_uiMethod == LW_VMM_ABORT_METHOD_WRITE)) {
  77. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_PERM; /* 权限错误 */
  78. }
  79. break;
  80. case EXC_SERROR_INT:
  81. abtInfo.VMABT_uiMethod = 0;
  82. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_FATAL_ERROR; /* 致命错误 */
  83. break;
  84. case EXC_BREAKPOINT_LO:
  85. case EXC_BREAKPOINT:
  86. case EXC_SOFTWARE_STEP_LO:
  87. case EXC_SOFTWARE_STEP:
  88. case EXC_WATCHPOINT_LO:
  89. case EXC_WATCHPOINT:
  90. case EXC_BKPT_AARCH32:
  91. case EXC_VECTOR_CATCH_AARCH32:
  92. case EXC_BRK_AARCH64:
  93. abtInfo.VMABT_uiMethod = 0;
  94. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_BREAK; /* 断点异常 */
  95. #if LW_CFG_GDB_EN > 0
  96. if (API_DtraceBreakTrap(ulAbortAddr, LW_TRAP_BRKPT)
  97. == ERROR_NONE) { /* 进入调试接口断点处理 */
  98. return;
  99. }
  100. #endif /* LW_CFG_GDB_EN > 0 */
  101. break;
  102. default:
  103. break;
  104. }
  105. #if LW_CFG_CPU_EXC_HOOK_EN > 0
  106. if (bspCpuExcHook(ptcbCur,
  107. pregctx->REG_ulPC,
  108. ulAbortAddr,
  109. abtInfo.VMABT_uiType,
  110. abtInfo.VMABT_uiMethod)) {
  111. return;
  112. }
  113. #endif
  114. API_VmmAbortIsr(pregctx->REG_ulPC, ulAbortAddr, &abtInfo, ptcbCur);
  115. }

 以上是archSyncExcHandle函数,分析此函数

  1. uiExcClass = (uiExcType >> 26) & 0x3f;
  2. uiExcISS = uiExcType & 0x1ffffff;
  3. ulAbortAddr = pregctx->REG_ulPC;

首先EC是从26位开始的所以当前,并且占用7位,所以当前uiExcClasss是单独将其取出来。当发生异常时arm寄存器已经被保存到了任务控制块tcb中这里获取PC就获取了地址。

错误的类型众多,我们只关心发生缺页中断时产生的错误

  1. case EXC_DATA_ABORT_LO:
  2. case EXC_DATA_ABORT:
  3. ulAbortAddr = arm64MmuAbtFaultAddr();
  4. abtInfo.VMABT_uiMethod = (uiExcISS & 0x40) ? LW_VMM_ABORT_METHOD_WRITE : LW_VMM_ABORT_METHOD_READ;
  5. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_MAP; /* 数据错误 */
  6. if (((uiExcISS & 0xf) == 0xf) && (abtInfo.VMABT_uiMethod == LW_VMM_ABORT_METHOD_WRITE)) {
  7. abtInfo.VMABT_uiType = LW_VMM_ABORT_TYPE_PERM; /* 权限错误 */
  8. }
  9. break;

根据ESR_EL1寄存器的定义,这里的宏定义分别是

也就是手册中的 

根据手册中的说明此错误是真多mmu 错误和堆栈指针未对齐等 。具体类型需要根据ESR_EL1寄存中的ISS位进行判断,根据不同的指令错误,ISS位的含义也不相同,具体可以参考armv8手册。

在发生错误的时候访问异常,需要知道访问的地址

 ulAbortAddr            = arm64MmuAbtFaultAddr();
  1. ;/*********************************************************************************************************
  2. ; MMU 异常地址获取
  3. ;*********************************************************************************************************/
  4. FUNC_DEF(arm64MmuAbtFaultAddr)
  5. MRS X0 , FAR_EL1
  6. RET
  7. FUNC_END()
  8. FILE_END()

使用FAR_EL1 寄存器获取异常地址。

此时异常地址和返回地址有已经获得,最后调用 

   API_VmmAbortIsr(pregctx->REG_ulPC, ulAbortAddr, &abtInfo, ptcbCur);

函数对当前的同步异常进行具体处理。

次函数使用上是下边函数的包装。

 API_VmmAbortIsrEx(ulRetAddr, ulAbortAddr, pabtInfo, ptcb, __vmmAbortShell);

以下是 API_VmmAbortIsrEx函数。

  1. /*********************************************************************************************************
  2. ** 函数名称: API_VmmAbortIsrEx
  3. ** 功能描述: 当 MMU 产生访问失效时, 调用此函数(类似于中断服务函数)
  4. ** 输 入 : ulRetAddr 异常返回地址
  5. ** ulAbortAddr 异常地址 (异常类型相关)
  6. ** pabtInfo 异常类型
  7. ** ptcb 出现异常的线程控制块 (不能为 NULL)
  8. ** pfuncHandler 异常处理函数
  9. ** 输 出 : NONE
  10. ** 全局变量:
  11. ** 调用模块:
  12. *********************************************************************************************************/
  13. LW_API
  14. VOID API_VmmAbortIsrEx (addr_t ulRetAddr,
  15. addr_t ulAbortAddr,
  16. PLW_VMM_ABORT pabtInfo,
  17. PLW_CLASS_TCB ptcb,
  18. VOIDFUNCPTR pfuncHandler)
  19. {
  20. PLW_VMM_ABORT_CTX pabtctx;
  21. PLW_STACK pstkFailShell; /* 启动 fail shell 的堆栈点 */
  22. BYTE *pucStkNow; /* 记录还原堆栈点 */
  23. __vmmAbortFatalDetected(ulRetAddr, ulAbortAddr, pabtInfo, ptcb); /* 致命错误探测 */
  24. #if LW_CFG_VMM_EN > 0
  25. __vmmAbortStkOfDetected(ulRetAddr, ulAbortAddr, pabtInfo, ptcb); /* 是否堆栈溢出 */
  26. #endif /* LW_CFG_VMM_EN > 0 */
  27. __KERNEL_ENTER(); /* 进入内核 */
  28. /* 产生异常 */
  29. pucStkNow = (BYTE *)archCtxStackEnd(&ptcb->TCB_archRegCtx); /* 记录还原堆栈点 */
  30. #if CPU_STK_GROWTH == 0
  31. pucStkNow += sizeof(LW_STACK); /* 向空栈方向移动一个堆栈空间 */
  32. pucStkNow = (BYTE *)ROUND_UP(pucStkNow, ARCH_STK_ALIGN_SIZE);
  33. pabtctx = (PLW_VMM_ABORT_CTX)pucStkNow; /* 记录 PAGE_FAIL_CTX 位置 */
  34. pucStkNow += __ABTCTX_SIZE_ALIGN; /* 让出 PAGE_FAIL_CTX 空间 */
  35. #else
  36. pucStkNow -= __ABTCTX_SIZE_ALIGN; /* 让出 PAGE_FAIL_CTX 空间 */
  37. pucStkNow = (BYTE *)ROUND_DOWN(pucStkNow, ARCH_STK_ALIGN_SIZE);
  38. pabtctx = (PLW_VMM_ABORT_CTX)pucStkNow; /* 记录 PAGE_FAIL_CTX 位置 */
  39. pucStkNow -= sizeof(LW_STACK); /* 向空栈方向移动一个堆栈空间 */
  40. #endif
  41. pabtctx->ABTCTX_ptcb = ptcb;
  42. pabtctx->ABTCTX_ulRetAddr = ulRetAddr; /* 异常返回地址 */
  43. pabtctx->ABTCTX_ulAbortAddr = ulAbortAddr; /* 异常地址 (异常类型相关) */
  44. pabtctx->ABTCTX_abtInfo = *pabtInfo; /* 异常类型 */
  45. pabtctx->ABTCTX_archRegCtx = ptcb->TCB_archRegCtx;
  46. pabtctx->ABTCTX_iLastErrno = (errno_t)ptcb->TCB_ulLastError;
  47. pabtctx->ABTCTX_iKernelSpace = __KERNEL_SPACE_GET2(ptcb);
  48. pstkFailShell = archTaskCtxCreate(&ptcb->TCB_archRegCtx,
  49. (PTHREAD_START_ROUTINE)pfuncHandler,
  50. (PVOID)pabtctx,
  51. (PLW_STACK)pucStkNow,
  52. 0); /* 建立访问异常陷阱外壳环境 */
  53. archTaskCtxSetFp(pstkFailShell,
  54. &ptcb->TCB_archRegCtx,
  55. &pabtctx->ABTCTX_archRegCtx); /* 保存 fp, 使 callstack 正常 */
  56. _StackCheckGuard(ptcb); /* 堆栈警戒检查 */
  57. __KERNEL_EXIT(); /* 退出内核 */
  58. #if LW_CFG_CPU_FPU_EN > 0
  59. if (__ABTCTX_ABORT_TYPE(pabtctx) == LW_VMM_ABORT_TYPE_FPE) {
  60. if (ptcb->TCB_ulOption & LW_OPTION_THREAD_USED_FP) { /* 如果为 FPU 异常 */
  61. __ARCH_FPU_SAVE(ptcb->TCB_pvStackFP); /* 需要保存当前 FPU CTX */
  62. }
  63. }
  64. #endif /* LW_CFG_CPU_FPU_EN > 0 */
  65. #if LW_CFG_CPU_DSP_EN > 0
  66. if (__ABTCTX_ABORT_TYPE(pabtctx) == LW_VMM_ABORT_TYPE_DSPE) {
  67. if (ptcb->TCB_ulOption & LW_OPTION_THREAD_USED_DSP) { /* 如果为 DSP 异常 */
  68. __ARCH_DSP_SAVE(ptcb->TCB_pvStackDSP); /* 需要保存当前 DSP CTX */
  69. }
  70. }
  71. #endif /* LW_CFG_CPU_DSP_EN > 0 */
  72. }

首先获取了当前的任务的堆栈指针

pucStkNow = (BYTE *)archCtxStackEnd(&ptcb->TCB_archRegCtx);         /*  记录还原堆栈点              */

这里获取堆栈指针是要下边创建线程陷阱。

根据是低地址往高地址还是高地址往低地址,目前预留出异常返回结构体的空间

  1. #if CPU_STK_GROWTH == 0
  2. pucStkNow += sizeof(LW_STACK); /* 向空栈方向移动一个堆栈空间 */
  3. pucStkNow = (BYTE *)ROUND_UP(pucStkNow, ARCH_STK_ALIGN_SIZE);
  4. pabtctx = (PLW_VMM_ABORT_CTX)pucStkNow; /* 记录 PAGE_FAIL_CTX 位置 */
  5. pucStkNow += __ABTCTX_SIZE_ALIGN; /* 让出 PAGE_FAIL_CTX 空间 */
  6. #else
  7. pucStkNow -= __ABTCTX_SIZE_ALIGN; /* 让出 PAGE_FAIL_CTX 空间 */
  8. pucStkNow = (BYTE *)ROUND_DOWN(pucStkNow, ARCH_STK_ALIGN_SIZE);
  9. pabtctx = (PLW_VMM_ABORT_CTX)pucStkNow; /* 记录 PAGE_FAIL_CTX 位置 */
  10. pucStkNow -= sizeof(LW_STACK); /* 向空栈方向移动一个堆栈空间 */
  11. #endif

 然后对异常返回空间的值进行赋值

  1. pabtctx->ABTCTX_ptcb = ptcb;
  2. pabtctx->ABTCTX_ulRetAddr = ulRetAddr; /* 异常返回地址 */
  3. pabtctx->ABTCTX_ulAbortAddr = ulAbortAddr; /* 异常地址 (异常类型相关) */
  4. pabtctx->ABTCTX_abtInfo = *pabtInfo; /* 异常类型 */
  5. pabtctx->ABTCTX_archRegCtx = ptcb->TCB_archRegCtx;
  6. pabtctx->ABTCTX_iLastErrno = (errno_t)ptcb->TCB_ulLastError;
  7. pabtctx->ABTCTX_iKernelSpace = __KERNEL_SPACE_GET2(ptcb);

然后创建任务

  1. /*********************************************************************************************************
  2. ** 函数名称: archTaskCtxCreate
  3. ** 功能描述: 创建任务上下文
  4. ** 输 入 : pregctx 寄存器上下文
  5. ** pfuncTask 任务入口
  6. ** pvArg 入口参数
  7. ** pstkTop 初始化堆栈起点
  8. ** ulOpt 任务创建选项
  9. ** 输 出 : 初始化堆栈结束点
  10. ** 全局变量:
  11. ** 调用模块:
  12. ** 注 意 : 堆栈从高地址向低地址增长.
  13. *********************************************************************************************************/
  14. PLW_STACK archTaskCtxCreate (ARCH_REG_CTX *pregctx,
  15. PTHREAD_START_ROUTINE pfuncTask,
  16. PVOID pvArg,
  17. PLW_STACK pstkTop,
  18. ULONG ulOpt)
  19. {
  20. ARCH_FP_CTX *pfpctx;
  21. ARCH_REG_T ulPstate;
  22. INT i;
  23. pstkTop = (PLW_STACK)ROUND_DOWN(pstkTop, ARCH_STK_ALIGN_SIZE); /* 堆栈指针向下 16 字节对齐 */
  24. pfpctx = (ARCH_FP_CTX *)((PCHAR)pstkTop - sizeof(ARCH_FP_CTX));
  25. pfpctx->FP_ulFp = (ARCH_REG_T)LW_NULL;
  26. pfpctx->FP_ulLr = (ARCH_REG_T)LW_NULL;
  27. ulPstate = arm64GetNZCV() | /* 获得当前 NZCV 寄存器 */
  28. arm64GetDAIF(); /* 获得当前 DAIF 寄存器 */
  29. ulPstate &= ~M_PSTATE_I; /* 使能 IRQ */
  30. pregctx->REG_ulPstate = ulPstate;
  31. /*
  32. * 初始化寄存器上下文
  33. */
  34. for (i = 0; i < ARCH_GREG_NR; i++) {
  35. pregctx->REG_ulReg[i] = i;
  36. }
  37. pregctx->REG_ulSmallCtx = 1; /* 小上下文 */
  38. pregctx->REG_ulReg[0] = (ARCH_REG_T)pvArg;
  39. pregctx->REG_ulLR = (ARCH_REG_T)pfuncTask;
  40. pregctx->REG_ulPC = (ARCH_REG_T)pfuncTask;
  41. pregctx->REG_ulSP = (ARCH_REG_T)pfpctx;
  42. return ((PLW_STACK)pfpctx);
  43. }

 上面函数是在arm64 创建任务上下文,首先第一个参数将TCB控制块的上下文控制块,分配一个栈帧的空间,好执行完毕后返回,函授获取状态寄存器,给TCB中上下文控制块中的寄状态寄存器赋值,然后将参数赋值给Reg[0],需要执行的任务函数地址赋值给LR和PC。sp赋值单签的栈帧地址。

  1. archTaskCtxSetFp(pstkFailShell,
  2. &ptcb->TCB_archRegCtx,
  3. &pabtctx->ABTCTX_archRegCtx); /* 保存 fp, 使 callstack 正常 */

最后执行此函数就是将栈帧地址赋值给TCB上下文控制块中的栈帧变量。等待退出函数时tcb上下文控制块寄存器填写到arm寄存器中,然后开始执行__vmmAbortShell 任务。

  1. /*********************************************************************************************************
  2. ** 函数名称: __vmmAbortShell
  3. ** 功能描述: 当 MMU 产生访问失效时, 线程执行陷阱函数.
  4. ** 输 入 : pabtctx page fail 上下文
  5. ** 输 出 : NONE
  6. ** 全局变量:
  7. ** 调用模块:
  8. *********************************************************************************************************/
  9. static VOID __vmmAbortShell (PLW_VMM_ABORT_CTX pabtctx)
  10. {
  11. INTREG iregInterLevel;
  12. addr_t ulAbortAddr = pabtctx->ABTCTX_ulAbortAddr;
  13. ULONG ulFlag;
  14. REGISTER PLW_VMM_PAGE pvmpageVirtual;
  15. REGISTER PLW_VMM_PAGE pvmpagePhysical;
  16. REGISTER PLW_VMM_PAGE_PRIVATE pvmpagep;
  17. ULONG ulAllocPageNum;
  18. BOOL bSwapNeedLoad;
  19. addr_t ulVirtualPageAlign;
  20. ULONG ulError;
  21. INT iRet;
  22. PLW_CLASS_TCB ptcbCur; /* 当前任务控制块 */
  23. if (__KERNEL_ISENTER()) {
  24. __vmmAbortDump(pabtctx); /* 打印关键信息 */
  25. __vmmAbortKill(pabtctx);
  26. goto __abort_return;
  27. }
  28. LW_TCB_GET_CUR_SAFE(ptcbCur);
  29. __VMM_LOCK();
  30. _K_vmmStatus.VMMS_i64AbortCounter++;
  31. if ((__ABTCTX_ABORT_TYPE(pabtctx) != LW_VMM_ABORT_TYPE_MAP) &&
  32. (__ABTCTX_ABORT_TYPE(pabtctx) != LW_VMM_ABORT_TYPE_PERM)) {
  33. __VMM_UNLOCK();
  34. __vmmAbortAccess(pabtctx); /* 访问异常情况 */
  35. goto __abort_return; /* 不会运行到这里 */
  36. }
  37. pvmpageVirtual = __vmmAbortPageGet(ulAbortAddr); /* 获得对应虚拟内存控制块 */
  38. if (pvmpageVirtual) {
  39. pvmpagep = (PLW_VMM_PAGE_PRIVATE)pvmpageVirtual->PAGE_pvAreaCb;
  40. } else {
  41. __VMM_UNLOCK();
  42. __vmmAbortAccess(pabtctx); /* 访问异常 */
  43. goto __abort_return; /* 不会运行到这里 */
  44. }
  45. _K_vmmStatus.VMMS_i64PageFailCounter++; /* 缺页中断次数++ */
  46. ptcbCur->TCB_i64PageFailCounter++; /* 缺页中断次数++ */
  47. ulVirtualPageAlign = ulAbortAddr & LW_CFG_VMM_PAGE_MASK; /* 获得访问地址页边界 */
  48. ulError = __vmmLibGetFlag(ulVirtualPageAlign, &ulFlag); /* 获得异常地址的物理页面属性 */
  49. if (ulError == ERROR_NONE) { /* 存在物理页面正常 */
  50. if (__ABTCTX_ABORT_TYPE(pabtctx) == LW_VMM_ABORT_TYPE_MAP) { /* MAP 类型错误 */
  51. __VMM_UNLOCK();
  52. goto __abort_return; /* 页表已经正常, 可以访问 */
  53. }
  54. /* 写入异常 */
  55. if (__ABTCTX_ABORT_METHOD(pabtctx) == LW_VMM_ABORT_METHOD_WRITE) {
  56. if (__vmmAbortWriteProtect(pvmpageVirtual, /* 尝试 copy-on-write 处理 */
  57. ulVirtualPageAlign,
  58. ulError) == ERROR_NONE) { /* 进入写保护处理 */
  59. __VMM_UNLOCK();
  60. goto __abort_return;
  61. }
  62. }
  63. __VMM_UNLOCK();
  64. __vmmAbortAccess(pabtctx); /* 非法内存访问 */
  65. goto __abort_return; /* 不会运行到这里 */
  66. } else { /* 映射错误, 没有物理页面存在 */
  67. if (pvmpagep->PAGEP_iFlags & LW_VMM_SHARED_CHANGE) { /* 共享区间 */
  68. if (__vmmAbortShare(pvmpageVirtual,
  69. pvmpagep,
  70. ulVirtualPageAlign) == ERROR_NONE) { /* 尝试共享 */
  71. __VMM_UNLOCK();
  72. goto __abort_return;
  73. }
  74. }
  75. ulAllocPageNum = __PAGEFAIL_ALLOC_PAGE_NUM; /* 缺页中断分配的内存页面个数 */
  76. pvmpagePhysical = __vmmAbortNewPage(ulAllocPageNum); /* 分配物理页面 */
  77. if (pvmpagePhysical == LW_NULL) {
  78. __VMM_UNLOCK();
  79. __ABTCTX_ABORT_TYPE(pabtctx) = LW_VMM_ABORT_TYPE_NOINFO; /* 缺少物理页面 */
  80. printk(KERN_CRIT "kernel no more physical page.\n"); /* 系统无法分配物理页面 */
  81. __vmmAbortKill(pabtctx);
  82. goto __abort_return;
  83. }
  84. bSwapNeedLoad = __vmmPageSwapIsNeedLoad(__PAGEFAIL_CUR_PID,
  85. ulVirtualPageAlign); /* 检查是否需要 load swap 数据 */
  86. if (bSwapNeedLoad) {
  87. iRet = __vmmAbortSwapPage(pvmpagePhysical,
  88. ulVirtualPageAlign,
  89. ulAllocPageNum); /* 进行页面交换处理 */
  90. } else {
  91. iRet = __vmmAbortNoPage(pvmpagePhysical,
  92. pvmpagep,
  93. ulVirtualPageAlign,
  94. ulAllocPageNum); /* 进行页面填充处理 */
  95. }
  96. if (iRet != ERROR_NONE) {
  97. __vmmPhysicalPageFree(pvmpagePhysical);
  98. __VMM_UNLOCK();
  99. __vmmAbortKill(pabtctx);
  100. goto __abort_return;
  101. }
  102. }
  103. ulError = __vmmLibPageMap(pvmpagePhysical->PAGE_ulPageAddr, /* 与分配时相同的属性 */
  104. ulVirtualPageAlign,
  105. ulAllocPageNum,
  106. pvmpageVirtual->PAGE_ulFlags); /* 映射指定的虚拟地址 */
  107. if (ulError) {
  108. _K_vmmStatus.VMMS_i64MapErrCounter++;
  109. __vmmPhysicalPageFree(pvmpagePhysical);
  110. __VMM_UNLOCK();
  111. printk(KERN_CRIT "kernel physical page map error.\n"); /* 系统无法映射物理页面 */
  112. __vmmAbortKill(pabtctx);
  113. goto __abort_return;
  114. }
  115. pvmpagePhysical->PAGE_ulMapPageAddr = ulVirtualPageAlign;
  116. pvmpagePhysical->PAGE_ulFlags = pvmpageVirtual->PAGE_ulFlags;
  117. __pageLink(pvmpageVirtual, pvmpagePhysical); /* 建立连接关系 */
  118. __VMM_UNLOCK();
  119. __abort_return:
  120. __KERNEL_SPACE_SET(pabtctx->ABTCTX_iKernelSpace); /* 恢复成进入之前的状态 */
  121. errno = pabtctx->ABTCTX_iLastErrno; /* 恢复之前的 errno */
  122. iregInterLevel = KN_INT_DISABLE(); /* 关闭当前 CPU 中断 */
  123. KN_SMP_MB();
  124. archSigCtxLoad(&pabtctx->ABTCTX_archRegCtx); /* 从 page fail 上下文中返回 */
  125. KN_INT_ENABLE(iregInterLevel); /* 运行不到这里 */
  126. }

首先获取发生异常页面的属性

  1. ULONG __vmmLibGetFlag (addr_t ulVirtualAddr, ULONG *pulFlag)
  2. {
  3. PLW_MMU_CONTEXT pmmuctx = __vmmGetCurCtx();
  4. ULONG ulFlag;
  5. ulFlag = __VMM_MMU_FLAG_GET(pmmuctx, ulVirtualAddr);
  6. if (pulFlag) {
  7. *pulFlag = ulFlag;
  8. }
  9. if (ulFlag & LW_VMM_FLAG_VALID) {
  10. return (ERROR_NONE);
  11. } else {
  12. _ErrorHandle(ERROR_VMM_PAGE_INVAL);
  13. return (ERROR_VMM_PAGE_INVAL);
  14. }
  15. }

 此函数会查证映射是否有效,其实是调用的mmu函数的的属性get函数,在arm64 中对应

  1. static ULONG arm64MmuFlagGet (PLW_MMU_CONTEXT pmmuctx, addr_t ulAddr)
  2. {
  3. LW_PGD_TRANSENTRY *p_pgdentry = arm64MmuPgdOffset(pmmuctx, ulAddr);/* 获取一级描述符 */
  4. INT iDescType;
  5. ULONG ulFlag = 0;
  6. UINT8 ucGuard; /* 严格的权限检查 */
  7. UINT8 ucXN; /* 可执行权限标志 */
  8. UINT8 ucPXN; /* 特权可执行权限标志 */
  9. UINT8 ucCon; /* Contiguous 标志 */
  10. UINT8 ucnG; /* nG 标志 */
  11. UINT8 ucAF; /* 是否拥有访问权限标志 */
  12. UINT8 ucSH; /* 共享权限标志 */
  13. UINT8 ucAP; /* 是否可写权限标志 */
  14. UINT8 ucNS; /* Non-Secure 标志 */
  15. UINT8 ucAIn;
  16. iDescType = (*p_pgdentry) & 0x03; /* 获得一级页表类型 */
  17. if (iDescType == ARM64_PGD_TYPE_BLOCK) { /* 基于段的映射 */
  18. return (LW_VMM_FLAG_UNVALID);
  19. } else if (iDescType == ARM64_PGD_TYPE_TABLE) { /* 基于四级页表映射 */
  20. LW_PMD_TRANSENTRY *p_pmdentry = arm64MmuPmdOffset((LW_PMD_TRANSENTRY *)p_pgdentry,
  21. ulAddr); /* 获取二级描述符 */
  22. if (arm64MmuPmdIsOk(*p_pmdentry)) {
  23. LW_PTS_TRANSENTRY *p_ptsentry = arm64MmuPtsOffset((LW_PTS_TRANSENTRY *)p_pmdentry,
  24. ulAddr);
  25. /* 获取三级描述符 */
  26. if (arm64MmuPtsIsOk(*p_ptsentry)) {
  27. LW_PTE_TRANSENTRY *p_pteentry = arm64MmuPteOffset((LW_PTE_TRANSENTRY *)p_ptsentry,
  28. ulAddr);
  29. /* 获取四级描述符 */
  30. if (arm64MmuPteIsOk(*p_pteentry)) {
  31. UINT64 u64Descriptor = (UINT64)(*p_pteentry);
  32. ucGuard = (UINT8)((u64Descriptor & ARM64_PTE_GUARD_MASK) >> ARM64_PTE_GUARD_SHIFT);
  33. ucXN = (UINT8)((u64Descriptor & ARM64_PTE_UXN_MASK) >> ARM64_PTE_UXN_SHIFT);
  34. ucPXN = (UINT8)((u64Descriptor & ARM64_PTE_PXN_MASK) >> ARM64_PTE_PXN_SHIFT);
  35. ucCon = (UINT8)((u64Descriptor & ARM64_PTE_CONT_MASK) >> ARM64_PTE_CONT_SHIFT);
  36. ucnG = (UINT8)((u64Descriptor & ARM64_PTE_NG_MASK) >> ARM64_PTE_NG_SHIFT);
  37. ucAF = (UINT8)((u64Descriptor & ARM64_PTE_AF_MASK) >> ARM64_PTE_AF_SHIFT);
  38. ucSH = (UINT8)((u64Descriptor & ARM64_PTE_SH_MASK) >> ARM64_PTE_SH_SHIFT);
  39. ucAP = (UINT8)((u64Descriptor & ARM64_PTE_AP_MASK) >> ARM64_PTE_AP_SHIFT);
  40. ucNS = (UINT8)((u64Descriptor & ARM64_PTE_NS_MASK) >> ARM64_PTE_NS_SHIFT);
  41. ucAIn = (UINT8)((u64Descriptor & ARM64_PTE_AIN_MASK) >> ARM64_PTE_AIN_SHIFT);
  42. arm64MmuAttr2Flags(ucGuard, ucXN, ucPXN, ucCon, ucnG,
  43. ucAF, ucSH, ucAP, ucNS, ucAIn, &ulFlag);
  44. return (ulFlag);
  45. }
  46. }
  47. }
  48. }
  49. return (LW_VMM_FLAG_UNVALID);
  50. }

可以看出,当pmd或者pts,pts中不存在此虚拟地址时返回无映射,此时在__vmmAbortShell函数中走else分支

首选是检查是否有共享页面。 共享页面是mmap 函数中用到。这里先不讨论共享页面。

不需要共享页面时,首先分配物理页面,然后将物理页面和虚拟页面建立映射。

在最后页面映射完成后,需要重新返回发生缺页中断的线程调用加载函数

在之前创建陷阱时已经将进程的上下文信息保存在pabtctx中,所以此时将上下文恢复到寄存器中,继续原来程序执行。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/553359
推荐阅读
相关标签
  

闽ICP备14008679号