3月第一讲蓬莱enclave-spmp编译和运行说明

编译penglai-enclave-driver

进入penglai-enclave-driver目录:

highlighter- reasonml

cd ~/dev/penglai-enclave-driver
#modify source path
sed -i 's|make -C ../openeuler-kernel/ ARCH=riscv M=$(PWD) modules|make -C /usr/lib/modules/$(shell uname -r)/build ARCH=riscv M=$(PWD) modules|' Makefile > /dev/null 2>&1
make -j$(nproc)
insmod penglai.ko

oe中编译sdk目录中相关库和demo

进入~/dev/sdk目录编译penglai-sdk:

highlighter- vim

cd ~/dev/sdk
./replace_compiler_prefix.sh
PENGLAI_SDK=$(pwd) make -j8

运行demo测试

highlighter- gradle

cd ~/dev/sdk/demo
./host/host count/count

oe中编译secGear程序

进入~/dev/secGear目录

highlighter- routeros

cd ~/dev/secGear && source environment && mkdir -p debug && cd debug
cmake -DENCLAVE=PL -DSDK_PATH=/root/dev/sdk .. && make && make install

运行demo测试

highlighter- awk

cd ~/dev/secGear/debug
./bin/secgear_helloworld
./bin/secgear_calculation

内核启动到用户程序启动的流程:

highlighter- mipsasm

main
├── uart_init
├── mm_init
├── arch_interrupt_init
├── create_root_thread
│   ├── create_root_cap_group
│   ├── __create_root_thread
│   └── switch_to_thread
└── eret_to_thread
    └── switch_context
  1. Chcore 启动后会依次初始化 uart 模块、内存管理模块、中断模块
  2. 然后调用 create_root_thread 创建一个根进程,创建进程的 cap_group 结构体并初始化,包括分配一块虚拟地址空间 vmspace和 slot_table 的初始化,__create_root_thread会先从磁盘中载入 ELF 文件,为进程创建一个主线程,最后将线程root_thread放入根进程中切换到线程执行;
  3. eret_to_thread 则switch_context完成从内核模式到用户模式的切换,并在用户模式下开始运行用户代码。

sys_create_cap_group()

这段代码是一个系统调用函数,名为 sys_create_cap_group,用于创建一个新的能力组(cap_group),并将其分配给指定的进程(pid)。

在这个函数中,首先检查当前的能力组是否为 ROOT_PID(即根能力组),如果不是,则返回错误码 -EPERM。

接下来,通过 obj_alloc 函数分配一个新的能力组结构体(new_cap_group),如果分配失败,则返回错误码 -ENOMEM。

然后,通过 cap_group_init 函数初始化新的能力组,将其与基本对象编号(BASE_OBJECT_NUM)和指定的进程 ID(pid)关联起来。

接着,通过 cap_alloc 函数在当前的能力组中分配一个新的能力(cap),并将其与新的能力组(new_cap_group)关联起来。如果分配失败,则返回错误码 -1。

然后,通过 cap_copy 函数将新的能力组(new_cap_group)复制到当前线程的能力组中,并将新的能力组设为第一个能力(cap[0])。

接下来,通过 obj_alloc 函数分配一个新的虚拟内存空间结构体(vmspace),如果分配失败,则返回错误码 -ENOMEM。

然后,通过 vmspace_init 函数初始化新的虚拟内存空间,并将其与指定的进程 ID(pid)关联起来。

接着,通过 cap_alloc 函数在新的能力组中分配一个新的能力(cap),并将其与新的虚拟内存空间(vmspace)关联起来。如果分配失败,则返回错误码 -1。

最后,通过 copy_from_user 函数将指定的能力组名称(cap_group_name)复制到新的能力组结构体(new_cap_group)中,并返回新的能力(cap)。

如果出现任何错误,则会释放之前分配的对象并返回相应的错误码。

Capability

Capability 可以理解为 Linux 下的文件描述符。它把一个资源对象和访问权限封装到了一起,并对外提供一个整形 cap 做访问的句柄(句柄就是对资源对象的指针或者引用的一种抽象)

ChCore 中每个 capability 都属于一个进程。cap 的值实际上就是对象在所属的 process 的 slot_table 中的下标。

示例代码:

c

// 仅为演示,删掉了部分异常处理的代码
// 分配 cap
int sys_create_pmo(u64 size, u64 type)
{
	int cap;
	struct pmobject *pmo;

	pmo = obj_alloc(TYPE_PMO, sizeof(*pmo)); // 分配对象
	pmo_init(pmo, type, size, 0); // 初始化
	cap = cap_alloc(current_process, pmo, 0); // 挂载到进程上,分配 cap 编号
    return cap;
}

// 使用 cap
int sys_map_pmo(u64 target_process_cap, u64 pmo_cap, u64 addr, u64 perm)
{
	struct pmobject *pmo;

	// 根据 cap 获取对象的指针
    pmo = obj_get(current_process, pmo_cap, TYPE_PMO);
    
    // 操作对象,省略之
    // ......
    
    // 声明自己操作结束,为了并发安全准备的。
    obj_put(pmo);
}

下面可以看几个例子感受一下cap是如何被使用的。

创建object并分配cap

thread.h的 create_thread函数中,我们需要创建线程,然后把线程加入到进程的slot_table中管理起来,同时要返回cap作为索引。其中核心的一句如下:

c

thread = obj_alloc(TYPE_THREAD, sizeof(*thread));

任何需要通过cap来管理的资源都是通过object来抽象的,所以需要先创建一个object对象。该函数的第二个参数是线程的大小,这是因为我们需要用这个大小来初始化object,使其能容纳我们需要的资源。

仔细看一下这个函数的定义:

c

void *obj_alloc(u64 type, u64 size)
{
	u64 total_size;
	struct object *object;
    
	total_size = sizeof(*object) + size;
	object = kmalloc(total_size);
	if (!object)
		return NULL;

	object->type = type;
	object->size = size;
	object->refcount = 0;
    /*
     * If the cap of the object is copied, then the copied cap (slot) is
     * stored in such a list.
     */
	init_list_head(&object->copies_head);

	return object->opaque;
}

https://www.iqiyi.com/v_21is04yum94.html
https://m.iqiyi.com/v_21is04yum94.html
https://www.iqiyi.com/v_2clubpj56m4.html
https://m.iqiyi.com/v_2clubpj56m4.html

在分配object的时候,kmalloc的大小为sizeof(*object)+size,最后返回的是opaque字段。此时object的内存布局如下:

object

语句:thread = obj_alloc(TYPE_THREAD, sizeof(*thread));,相当于是thread = malloc(sizeof(struct thread)),并且额外地,在头部加上了点别的信息,这样组成了一个object。这样一个函数调用完成了thread空间的分配和object的初始化。

这里最精妙的地方就是这个opaque的类型,是个数组,因此最后返回这个数组名的时候,实际上返回的是指向第一个元素的指针,即第二个参数size分配的额外的内存空间的起始地址。如果换成u64*指针类型的话就没有这种效果了。

然后我们通过cap = cap_alloc(process, thread, 0);,将线程加入到进程的slot_table中,并且返回cap,最终返回给用户,

根据cap获取对应的被管理的对象(线程,pmo等)

如何根据线程的cap来获取线程本身?在cap_group函数中有如下语句:root_thread = obj_get(root_process, thread_cap, TYPE_THREAD);

PMO(Physical Memory Object)

物理内存对象

  • 34
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值