OpenSBI的Domain支持

概述

OpenSBI域(Domain)是将底层硬件划分为系统级的分区,每个分区拥有自己的内存区域(RAM和MMIO设备)和HARTs(硬件线程)。OpenSBI将利用RISC-V架构的特性(如PMP、ePMP、IOPMP、SiFive Shield等)来实现域之间的安全隔离。

OpenSBI域三个重要的结构:

  • struct sbi_domain_memregion:域的内存区域
  • struct sbi_hartmask:域的HART集合
  • struct sbi_domain:域的实例

每个HART都必须分配一个OpenSBI域,OpenSBI负责填充域和映射域到对应HART。OpenSBI平台默认将所有HARTs分配给ROOT域,除非有特定的平台需求,一般并不需要手动配置域。

域的内存区域

域的内存区域使用struct_sbi_domain_memregion结构体表示:

/** Representation of OpenSBI domain memory region */
struct sbi_domain_memregion {
	/**
	 * Size of memory region as power of 2
	 * It has to be minimum 3 and maximum __riscv_xlen
	 */
	unsigned long order;
	/**
	 * Base address of memory region
	 * It must be 2^order aligned address
	 */
	unsigned long base;
	/** Flags representing memory region attributes */
	unsigned long flags;
}
  • order:内存区域大小为2order
  • base:内存区域基地址
  • flags:内存区域标志,表示内存类型(RAM或者MMIO)和访问权限(读、写和执行等)

域实例

域实例使用struct sbi_domain结构体表示:

/** Representation of OpenSBI domain */
struct sbi_domain {
	/**
	 * Logical index of this domain
	 * Note: This set by sbi_domain_finalize() in the coldboot path
	 */
	u32 index;
	/**
	 * HARTs assigned to this domain
	 * Note: This set by sbi_domain_init() and sbi_domain_finalize()
	 * in the coldboot path
	 */
	struct sbi_hartmask assigned_harts;
	/** Spinlock for accessing assigned_harts */
	spinlock_t assigned_harts_lock;
	/** Name of this domain */
	char name[64];
	/** Possible HARTs in this domain */
	const struct sbi_hartmask *possible_harts;
	/** Contexts for possible HARTs indexed by hartindex */
	struct sbi_context *hartindex_to_context_table[SBI_HARTMASK_MAX_BITS];
	/** Array of memory regions terminated by a region with order zero */
	struct sbi_domain_memregion *regions;
	/** HART id of the HART booting this domain */
	u32 boot_hartid;
	/** Arg1 (or 'a1' register) of next booting stage for this domain */
	unsigned long next_arg1;
	/** Address of next booting stage for this domain */
	unsigned long next_addr;
	/** Privilege mode of next booting stage for this domain */
	unsigned long next_mode;
	/** Is domain allowed to reset the system */
	bool system_reset_allowed;
	/** Is domain allowed to suspend the system */
	bool system_suspend_allowed;
	/** Identifies whether to include the firmware region */
	bool fw_region_inited;
};
  • index:域的逻辑索引
  • name:域的名称
  • assigned_harts:分配给此域的HART
  • possible_harts:此域中可能的HART
  • hartindex_to_context_table:对应可能的HART的上下文
  • regions:内存区域数组,以order=0的内存区域结束
  • boot_hartid:启动此域的HART ID
  • next_addr:此域下一个启动阶段的地址
  • next_arg1:下一个启动阶段的arg1参数
  • next_mode:此域下一个启动阶段的特权模式,可以是S模式或U模式
  • system_reset_allowed:此域是否允许复位系统
  • system_suspend_allowed:此域是否允许挂起系统

对于struct sbi_domain结构体中的regions参数,为了与PMP要求对齐,有一些约束限制:

  • 必须存在一个内存区域来保护OpenSBI固件,使其不能被S模式和U模式进行访问
  • 对于两个重叠内存区域,必须是一个区域包含另外一个区域;内存区域大小不能一样;内存区域标志不能相同
  • 当进行内存访问检查时,如果存在重叠的地址范围,优先考虑最小内存区域的标志

根域

在OpenSBI中,ROOT域是一个特殊的默认域,其自动分配给所有的HART。OpenSBI在启动早期会配置这个域,如下:

  • index:ROOT域的逻辑索引始终为零,表示它是系统中的第一个最基本的域
  • name:ROOT域的名称固定为"root"
  • assigned_harts:在启动时,所有有效的HART都被默认分配给ROOT域。然而,后续这个分配可能会发生变化,但ROOT域始终保留作为系统的基础
  • possible_harts:ROOT域可能的HART集合,这意味着理论上任何HART都可以被分配给ROOT域(尽管实际分配可能有所不同)
  • hartindex_to_context_table:这是一个上下文表,映射了ROOT域中每个可能HART的上下文信息
  • regions:ROOT域通常有两个内存区域:
    • 一个是用于保护OpenSBI固件免受S模式和U模式访问的内存区域
    • 一个是覆盖整个内存地址空间的内存区域(大小等于__riscv_xlen定义的位宽),允许S模式和U模式访问所有内存
  • boot_hartid:启动此ROOT域的冷启动HART
  • next_addrnext_arg1next_mode:这些字段从冷启动HART的暂存空间中获取,分别表示ROOT域下一个启动阶段的地址、arg1参数和特权模式
  • system_reset_allowedsystem_suspend_allowed:ROOT域允许复位和挂起系统,因为它是系统的基础管理域

域的影响

系统被划分为多个域后,会带来一些影响:

  • HART的域上下文:在任何时刻,一个HART都只能在一个OpenSBI域上下文中运行。这意味着HART的特权级别、内存访问权限和其他相关资源都受其所属域的限制。
  • SBI IPI和RFENCE调用:从HART A发出的SBI IPI和RFENCE调用仅限于HART A域中的HART。这有助于实现域间隔离,防止未经授权的通信。
  • SBI HSM调用:如果HART A尝试通过SBI HSM(硬件状态管理)调用来更改或读取HART B的状态,那么这些调用仅当HART A和HART B被分配到同一个域时才有效。这确保了只有属于同一管理域的HART才能相互管理和交互。
  • 内存访问权限:在S模式或U模式下运行的HART只能访问其所属域定义的内存区域。这通过物理内存保护(PMP)等机制来实现,确保了不同域之间的内存隔离和安全性。

代码

在OpenSBI的启动代码中,建立了一个ROOT域并进行了初始化:

int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid)
{
	u32 i;
	int rc;
	struct sbi_hartmask *root_hmask;
	struct sbi_domain_memregion *root_memregs;
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);

	if (scratch->fw_rw_offset == 0 ||
	    (scratch->fw_rw_offset & (scratch->fw_rw_offset - 1)) != 0) {
		sbi_printf("%s: fw_rw_offset is not a power of 2 (0x%lx)\n",
			   __func__, scratch->fw_rw_offset);
		return SBI_EINVAL;
	}

	if ((scratch->fw_start & (scratch->fw_rw_offset - 1)) != 0) {
		sbi_printf("%s: fw_start and fw_rw_offset not aligned\n",
			   __func__);
		return SBI_EINVAL;
	}

	domain_hart_ptr_offset = sbi_scratch_alloc_type_offset(void *);
	if (!domain_hart_ptr_offset)
		return SBI_ENOMEM;

	root_memregs = sbi_calloc(sizeof(*root_memregs), ROOT_REGION_MAX + 1);
	if (!root_memregs) {
		sbi_printf("%s: no memory for root regions\n", __func__);
		rc = SBI_ENOMEM;
		goto fail_free_domain_hart_ptr_offset;
	}
	root.regions = root_memregs;

	root_hmask = sbi_zalloc(sizeof(*root_hmask));
	if (!root_hmask) {
		sbi_printf("%s: no memory for root hartmask\n", __func__);
		rc = SBI_ENOMEM;
		goto fail_free_root_memregs;
	}
	root.possible_harts = root_hmask;

	/* Root domain firmware memory region */
	sbi_domain_memregion_init(scratch->fw_start, scratch->fw_rw_offset,
				  (SBI_DOMAIN_MEMREGION_M_READABLE |
				   SBI_DOMAIN_MEMREGION_M_EXECUTABLE),
				  &root_memregs[root_memregs_count++]);

	sbi_domain_memregion_init((scratch->fw_start + scratch->fw_rw_offset),
				  (scratch->fw_size - scratch->fw_rw_offset),
				  (SBI_DOMAIN_MEMREGION_M_READABLE |
				   SBI_DOMAIN_MEMREGION_M_WRITABLE),
				  &root_memregs[root_memregs_count++]);

	root.fw_region_inited = true;

	/*
	 * Allow SU RWX on rest of the memory region. Since pmp entries
	 * have implicit priority on index, previous entries will
	 * deny access to SU on M-mode region. Also, M-mode will not
	 * have access to SU region while previous entries will allow
	 * access to M-mode regions.
	 */
	sbi_domain_memregion_init(0, ~0UL,
				  (SBI_DOMAIN_MEMREGION_SU_READABLE |
				   SBI_DOMAIN_MEMREGION_SU_WRITABLE |
				   SBI_DOMAIN_MEMREGION_SU_EXECUTABLE),
				  &root_memregs[root_memregs_count++]);

	/* Root domain memory region end */
	root_memregs[root_memregs_count].order = 0;

	/* Root domain boot HART id is same as coldboot HART id */
	root.boot_hartid = cold_hartid;

	/* Root domain next booting stage details */
	root.next_arg1 = scratch->next_arg1;
	root.next_addr = scratch->next_addr;
	root.next_mode = scratch->next_mode;

	/* Root domain possible and assigned HARTs */
	for (i = 0; i < plat->hart_count; i++)
		sbi_hartmask_set_hartindex(i, root_hmask);

	/* Finally register the root domain */
	rc = sbi_domain_register(&root, root_hmask);
	if (rc)
		goto fail_free_root_hmask;

	return 0;

fail_free_root_hmask:
	sbi_free(root_hmask);
fail_free_root_memregs:
	sbi_free(root_memregs);
fail_free_domain_hart_ptr_offset:
	sbi_scratch_free_offset(domain_hart_ptr_offset);
	return rc;
}

上面的代码中建立了三个针对RAM空间的内存区域:OpenSBI代码段、数据段以及其他剩余内存区域。其余的内存区域是针对MMIO设备,是在设备初始化中添加到ROOT域中,如UART设备通过调用sbi_domain_root_add_memrange函数添加:

int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
		  u32 reg_width, u32 reg_offset)
{
	u16 bdiv = 0;

	uart8250_base      = (volatile char *)base + reg_offset;
	uart8250_reg_shift = reg_shift;
	uart8250_reg_width = reg_width;
	uart8250_in_freq   = in_freq;
	uart8250_baudrate  = baudrate;

	if (uart8250_baudrate) {
		bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
		       (16 * uart8250_baudrate);
	}

	/* Disable all interrupts */
	set_reg(UART_IER_OFFSET, 0x00);
	/* Enable DLAB */
	set_reg(UART_LCR_OFFSET, 0x80);

	if (bdiv) {
		/* Set divisor low byte */
		set_reg(UART_DLL_OFFSET, bdiv & 0xff);
		/* Set divisor high byte */
		set_reg(UART_DLM_OFFSET, (bdiv >> 8) & 0xff);
	}

	/* 8 bits, no parity, one stop bit */
	set_reg(UART_LCR_OFFSET, 0x03);
	/* Enable FIFO */
	set_reg(UART_FCR_OFFSET, 0x01);
	/* No modem control DTR RTS */
	set_reg(UART_MCR_OFFSET, 0x00);
	/* Clear line status */
	get_reg(UART_LSR_OFFSET);
	/* Read receive buffer */
	get_reg(UART_RBR_OFFSET);
	/* Set scratchpad */
	set_reg(UART_SCR_OFFSET, 0x00);

	sbi_console_set_device(&uart8250_console);

	return sbi_domain_root_add_memrange(base, PAGE_SIZE, PAGE_SIZE,
					    (SBI_DOMAIN_MEMREGION_MMIO |
					    SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW));
}

以Qemu启动OpenSBI为例,关于根域的打印信息如下:

Domain0 Name              : root
Domain0 Boot HART         : 0
Domain0 HARTs             : 0*
Domain0 Region00          : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region01          : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region02          : 0x0000000080040000-0x000000008005ffff M: (R,W) S/U: ()
Domain0 Region03          : 0x0000000080000000-0x000000008003ffff M: (R,X) S/U: ()
Domain0 Region04          : 0x000000000c000000-0x000000000fffffff M: (I,R,W) S/U: (R,W)
Domain0 Region05          : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address      : 0x0000000080200000
Domain0 Next Arg1         : 0x0000000082200000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes
Domain0 SysSuspend        : yes

可以看出ROOT域共建立了六个内存区域:

区域号地址标志说明
00x10000000~0x10000FFFSBI_DOMAIN_MEMREGION_MMIO | SBI_DOMAIN_MEMREGION_SHARED_SURW_MRWUART设备,MMIO类型,M/S/U模式均可读写
10x02000000~0x0200FFFFSBI_DOMAIN_MEMREGION_MMIO | SBI_DOMAIN_MEMREGION_M_READABLE | SBI_DOMAIN_MEMREGION_M_WRITABLECLINT设备,MMIO类型,只M模式可读写
20x80040000~0x8005FFFFSBI_DOMAIN_MEMREGION_M_READABLE | SBI_DOMAIN_MEMREGION_M_EXECUTABLEOpenSBI数据段,只M模式可读写
30x80000000~0x8003FFFFSBI_DOMAIN_MEMREGION_M_READABLE | SBI_DOMAIN_MEMREGION_M_WRITABLEOpenSBI代码段,只M模式可读和可执行
40x0C000000~0x0FFFFFFFSBI_DOMAIN_MEMREGION_MMIO | SBI_DOMAIN_MEMREGION_SHARED_SURW_MRWPLIC设备,MMIO类型,M/S/U模式均可读写
50x0~0xFFFFFFFFFFFFFFFFSBI_DOMAIN_MEMREGION_SU_READABLE | SBI_DOMAIN_MEMREGION_SU_WRITABLE | SBI_DOMAIN_MEMREGION_SU_EXECUTABLE其他剩余内存区域,只允许S/U模式读写和执行

上面索引为23的内存区域用于保护OpenSBI固件免受S模式和U模式访问,而索引5的剩余内存区域,如果想实现只有S/U模式访问,M模式不可访问的效果,需要借助RISC-V的SMEPMP机制实现。

参考

  1. domain_support

欢迎关注“安全有理”微信公众号。

安全有理

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值