• RISC-V架构——物理内存保护机制设置函数(pmp_set)解析


    1、物理内存保护机制

    参考博客:《RISC-V架构——物理内存属性和物理内存保护》

    2、pmp_set函数源码

    int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
    	    unsigned long log2len)
    {
    	int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr;
    	unsigned long cfgmask, pmpcfg;
    	unsigned long addrmask, pmpaddr;
    
    	/* check parameters */
    	if (n >= PMP_COUNT || log2len > __riscv_xlen || log2len < PMP_SHIFT)
    		return SBI_EINVAL;
    
    	/* 计算配置项所在pmpcfg是哪一个,已经配置项在pmpcfg中的偏移量 */
    #if __riscv_xlen == 32
    	pmpcfg_csr   = CSR_PMPCFG0 + (n >> 2);
    	pmpcfg_shift = (n & 3) << 3;
    #elif __riscv_xlen == 64
    	pmpcfg_csr   = (CSR_PMPCFG0 + (n >> 2)) & ~1;
    	pmpcfg_shift = (n & 7) << 3;
    #else
    # error "Unexpected __riscv_xlen"
    #endif
    	pmpaddr_csr = CSR_PMPADDR0 + n;
    
    	//清除配置项里地址匹配模式
    	prot &= ~PMP_A;	
    	//如果是要设置的物理内存是4个字节则用NA4模式,否则用NAPOT模式
    	prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT;	
    	cfgmask = ~(0xffUL << pmpcfg_shift);
    	//读取出对于pmpcfg寄存器的值,并且将对应表项清零
    	pmpcfg	= (csr_read_num(pmpcfg_csr) & cfgmask);
    	//设置对应表项
    	pmpcfg |= ((prot << pmpcfg_shift) & ~cfgmask);
    
    	/* 配置pmp地址寄存器,如果要设置的物理地址范围是4字节 */
    	if (log2len == PMP_SHIFT) {	
    		pmpaddr = (addr >> PMP_SHIFT);
    	} else {	
    		if (log2len == __riscv_xlen) {	//__riscv_xlen是寄存器的位宽,相等则是要设置整个物理地址空间
    			pmpaddr = -1UL;
    		} else {
    			addrmask = (1UL << (log2len - PMP_SHIFT)) - 1;	
    			pmpaddr	 = ((addr >> PMP_SHIFT) & ~addrmask); //将要设置的物理地址范围左移2位,然后低(log2len - 2)位清0
    			pmpaddr |= (addrmask >> 1);	//设置pmp地址寄存器,此时pmpaddr寄存器的值,最低位开始有(log2len - 3)个1
    		}
    	}
    
    	/* 设置pmp表项和pmp地址寄存器 */
    	csr_write_num(pmpaddr_csr, pmpaddr);
    	csr_write_num(pmpcfg_csr, pmpcfg);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    3、pmp_set函数分析

    3.1、函数传参分析

    在这里插入图片描述

    3.2、pmp配置表项设置

    在这里插入图片描述

    3.3、pmp地址寄存器的值计算

    在这里插入图片描述

    4、pmp_get函数源码

    int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
    	    unsigned long *log2len)
    {
    	int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr;
    	unsigned long cfgmask, pmpcfg, prot;
    	unsigned long t1, addr, len;
    
    	/* check parameters */
    	if (n >= PMP_COUNT || !prot_out || !addr_out || !log2len)
    		return SBI_EINVAL;
    	*prot_out = *addr_out = *log2len = 0;
    
    	/* calculate PMP register and offset */
    #if __riscv_xlen == 32
    	pmpcfg_csr   = CSR_PMPCFG0 + (n >> 2);
    	pmpcfg_shift = (n & 3) << 3;
    #elif __riscv_xlen == 64
    	pmpcfg_csr   = (CSR_PMPCFG0 + (n >> 2)) & ~1;
    	pmpcfg_shift = (n & 7) << 3;
    #else
    # error "Unexpected __riscv_xlen"
    #endif
    	pmpaddr_csr = CSR_PMPADDR0 + n;
    
    	/* decode PMP config */
    	cfgmask = (0xffUL << pmpcfg_shift);
    	pmpcfg	= csr_read_num(pmpcfg_csr) & cfgmask;
    	prot	= pmpcfg >> pmpcfg_shift;
    
    	/* decode PMP address */
    	if ((prot & PMP_A) == PMP_A_NAPOT) {
    		addr = csr_read_num(pmpaddr_csr);
    		if (addr == -1UL) {
    			addr	= 0;
    			len	= __riscv_xlen;
    		} else {
    			t1	= ctz(~addr);
    			addr	= (addr & ~((1UL << t1) - 1)) << PMP_SHIFT;
    			len	= (t1 + PMP_SHIFT + 1);
    		}
    	} else {
    		addr	= csr_read_num(pmpaddr_csr) << PMP_SHIFT;
    		len	= PMP_SHIFT;
    	}
    
    	/* return details */
    	*prot_out    = prot;
    	*addr_out    = addr;
    	*log2len     = len;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    pmp_get函数就是pmp_set函数的逆过程,解析出当前设置过哪些物理地址范围保护;

    注意事项

    pmp_set函数和pmp_get函数时摘抄自opensbi,还严格按照RISC-V规范编写的,但实际芯片在设计时并不会严格按照RISC-V规定的物理内存保护机制去设计,上面的代码移植到特定芯片平台时可能是要经过适配才行。就我遇到的情况,平头哥的玄铁C906就不能直接使用上面的函数来设置和获取物理内存保护。

  • 相关阅读:
    【翻译】Seastar 教程(三)
    [附源码]SSM计算机毕业设计ssm新冠疫苗预约接种信息管理JAVA
    【java】 对命名规范的思考——VO,BO,PO,DO,DTO是什么
    rpx是微信小程序独有的,解决屏幕自适应的尺寸单位
    数学建模2019B “同心协力”策略研究
    SQL Dblink SQL
    数据导入与预处理-课程总结-资料汇总贴
    JavaScript概述
    自动驾驶辅助系统性能评估工具MXeval4.1版本更新快讯
    【毕业设计】基于java+swing+GUI的雷电游戏GUI设计与实现(毕业论文+程序源码)——雷电游戏
  • 原文地址:https://blog.csdn.net/weixin_42031299/article/details/133990883