RISC-V MCU中文社区

【求助】 Nuclei 用户模式如何切换回机器模式

发表于 CM32M433R MCU 2024-07-02 14:16:01
3
586
0

N级别处理器内核从User Mode切换到Machine Mode只能通过异常、响应中断或者NMI的方式发生:

响应异常进入异常处理模式。请参见第3.4节了解其详情。

注意:软件可以通过调用ecall指令强行进入ecall异常处理函数。

响应中断进入中断处理模式。请参见第5.6节了解其详情。

响应NMI进入NMI处理模式。请参见第4.3节了解其详情。

根据说明,进入用户模式后,Led()执行ecall命令就会进入异常中断ECALL_Exception_Handler,这时候是在M模式了吗?代码怎么能从异常中断回到主函数继续执行while循环里的语句呢?


/** @addtogroup CM32M4xxR_StdPeriph_Examples
 * @{
 */
#define MACHINE_MODE_STACK_SIZE			0x400
static uint8_t sMachineModeStack[MACHINE_MODE_STACK_SIZE] = {0};

//Configure test_array Acess Permission on PMP
void PMP_Config() {
	PMP_Region_InitTypeDef pmp_init;

	pmp_init.Number = PMP_REGION_NUMBER0;
	pmp_init.Enable = PMP_REGION_ENABLE;   // Enable Configuration
	pmp_init.Lock = PMP_REGION_UNLOCK; //
	pmp_init.BaseAddress = 0;  //
	pmp_init.Size = PMP_REGION_SIZE_4GB;    //Setting array size
	pmp_init.AddressMatching = PMP_ADDRESS_MATCHING_NAPOT; //Setting PMP Size to NAPOT mode -> 2^n
	pmp_init.AccessPermission = PMP_ACCESS_RWX; //Setting array permission is Read Only

	PMP_ConfigRegion(&pmp_init);

	sPMP_Region_InitTypeDef spmp_init;
	spmp_init.Number = SPMP_REGION_NUMBER0;
	spmp_init.Enable = SPMP_REGION_ENABLE;
	spmp_init.Lock = SPMP_REGION_UNLOCK;
	spmp_init.BaseAddress = 0;
	spmp_init.Size = SPMP_REGION_SIZE_4GB;
	spmp_init.AddressMatching = SPMP_ADDRESS_MATCHING_NAPOT;
	spmp_init.UserMode = SPMP_USERMODE_RESET;
	spmp_init.AccessPermission = SPMP_ACCESS_RWX;

	sPMP_ConfigRegion(&spmp_init);
}
/**
 * @brief Jump User Mode
 */
void JumpUserMode(uint32_t func)
{
	__RV_CSR_WRITE(CSR_MSCRATCH, sMachineModeStack + MACHINE_MODE_STACK_SIZE);
	__RV_CSR_CLEAR(CSR_MSTATUS, MSTATUS_MPP);
	__RV_CSR_WRITE(CSR_MEPC, func);
	__ASM  volatile ( "mret ");
}

void ECALL_Exception_Handler(unsigned long mcause,unsigned long sp)
{
   uint32_t saved_regs = sp;
   uint32_t mepc = ((uint32_t *)saved_regs)[12];
   printf("ECALL Exception Trigger\r\n");
   ((uint32_t *)saved_regs)[12] = mepc + 4;
}

/**
 * @brief User Mode Application
 */
void Led()
{
	LedInit(LED2_PORT, LED2_PIN);
	__ECALL();
}

/**
 * @brief Main function
 */
int main(void) {
	/* System Clocks Configuration */
	PMP_Config();
        Exception_Register_EXC(UmodeEcall_EXCn, (unsigned long)ECALL_Exception_Handler);
	JumpUserMode((uint32_t)Led);
	while (1) {
            LedBlink(LED2_PORT, LED2_PIN);
	    delay_ms(1000);
	}
}



喜欢0
用户评论 (3)
  • dekira

    2024-07-03 17:03:13 dekira 1#

    yuxin

    但是用户模式下是不能访问mstatus和使用mret指令的,会引发非法指令异常

    在user mode,Led()调用Ecall会产生异常,然后进入machine mode

    异常会从MTVEC定义的PC地址开始执行。先保存现场,然后调用你定义的ECALL_Exception_Handler函数,这个函数执行完之后继续执行MTVEC代码,回现场,最后调用mret返回user mode,应该是这个过程。

    异常处理函数实际上不是你定义的ECALL_Exception_Handler函数,而是一个其他的函数,这个函数的入口地址被指定到MTVEC寄存器,你定义的ECALL_Exception_Handler函数只是在这个函数中调用了一次而已。

  • yuxin

    2024-07-03 15:42:57 yuxin 2#

    dekira

    mret指令


    但是用户模式下是不能访问mstatus和使用mret指令的,会引发非法指令异常

  • dekira

    2024-07-03 15:38:45 dekira 3#

    mret指令


yuxin

yuxin 实名认证

懒的都不写签名

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板