初始化程序
./init --tut untyped
cd untyped_build
ninja
./simulate
创建一个untyped capability
我们首次运行这一节的教程的时候会看到下面的输出,他列举除了所有这个root task被给予的untyped capability。
Booting all finished, dropped to user space
CSlot Paddr Size Type
0x139 0 2^20 device untyped
0x13a 0x1ffe0000 2^17 device untyped
0x13b 0x20000000 2^29 device untyped
0x13c 0x40000000 2^30 device untyped
0x13d 0x80000000 2^30 device untyped
0x13e 0xc0000000 2^29 device untyped
0x13f 0xe0000000 2^28 device untyped
0x140 0xf0000000 2^27 device untyped
0x141 0xf8000000 2^26 device untyped
0x142 0xfc000000 2^25 device untyped
0x143 0xfe000000 2^23 device untyped
0x144 0xfe800000 2^22 device untyped
0x145 0xfec01000 2^12 device untyped
0x146 0xfec02000 2^13 device untyped
0x147 0xfec04000 2^14 device untyped
0x148 0xfec08000 2^15 device untyped
0x149 0xfec10000 2^16 device untyped
0x14a 0xfec20000 2^17 device untyped
0x14b 0xfec40000 2^18 device untyped
0x14c 0xfec80000 2^19 device untyped
0x14d 0xfed00000 2^20 device untyped
0x14e 0xfee01000 2^12 device untyped
0x14f 0xfee02000 2^13 device untyped
0x150 0xfee04000 2^14 device untyped
0x151 0xfee08000 2^15 device untyped
0x152 0xfee10000 2^16 device untyped
0x153 0xfee20000 2^17 device untyped
0x154 0xfee40000 2^18 device untyped
0x155 0xfee80000 2^19 device untyped
0x156 0xfef00000 2^20 device untyped
0x157 0xff000000 2^23 device untyped
0x158 0xff800000 2^22 device untyped
0x159 0xffc00000 2^21 device untyped
0x15a 0xffe00000 2^20 device untyped
0x15b 0xfff00000 2^19 device untyped
0x15c 0xfff80000 2^18 device untyped
0x15d 0xfffc0000 2^17 device untyped
0x15e 0xfffe0000 2^16 device untyped
0x15f 0xffff0000 2^15 device untyped
0x160 0xffff8000 2^14 device untyped
0x161 0xffffc000 2^13 device untyped
0x162 0xffffe000 2^12 device untyped
0x163 0xfffff000 2^11 device untyped
0x164 0xfffff800 2^10 device untyped
0x165 0xfffffc00 2^9 device untyped
0x166 0xfffffe00 2^8 device untyped
0x167 0xffffff00 2^7 device untyped
0x168 0xffffff80 2^6 device untyped
0x169 0xffffffc0 2^5 device untyped
0x16a 0xffffffe0 2^4 device untyped
0x16b 0x100000000 2^32 device untyped
0x16c 0x200000000 2^33 device untyped
0x16d 0x400000000 2^34 device untyped
0x16e 0x800000000 2^35 device untyped
0x16f 0x1000000000 2^36 device untyped
0x170 0x2000000000 2^37 device untyped
0x171 0x4000000000 2^38 device untyped
0x172 0x8000000000 2^46 device untyped
0x173 0x408000000000 2^45 device untyped
0x174 0x608000000000 2^44 device untyped
0x175 0x708000000000 2^43 device untyped
0x176 0x788000000000 2^42 device untyped
0x177 0x7c8000000000 2^41 device untyped
0x178 0x7e8000000000 2^40 device untyped
0x179 0x7f8000000000 2^39 device untyped
0x17a 0x100000 2^20 untyped
0x17b 0x200000 2^21 untyped
0x17c 0x400000 2^22 untyped
0x17d 0xb38000 2^15 untyped
0x17e 0xb40000 2^18 untyped
0x17f 0xb80000 2^19 untyped
0x180 0xc00000 2^22 untyped
0x181 0x1000000 2^24 untyped
0x182 0x2000000 2^25 untyped
0x183 0x4000000 2^26 untyped
0x184 0x8000000 2^27 untyped
0x185 0x10000000 2^27 untyped
0x186 0x18000000 2^26 untyped
0x187 0x1c000000 2^25 untyped
0x188 0x1e000000 2^24 untyped
0x189 0x1f000000 2^23 untyped
0x18a 0x1f800000 2^22 untyped
0x18b 0x1fe08800 2^11 untyped
0x18c 0x1fe09000 2^12 untyped
0x18d 0x1fe0a000 2^13 untyped
0x18e 0x1fe0c000 2^14 untyped
0x18f 0x1fe10000 2^16 untyped
0x190 0x1fe20000 2^17 untyped
0x191 0x1fe40000 2^18 untyped
0x192 0x1fe80000 2^19 untyped
0x193 0x1ff00000 2^19 untyped
0x194 0x1ff80000 2^18 untyped
0x195 0x1ffc0000 2^17 untyped
在程序输出的log的末尾,有一个报错:
<<seL4(CPU 0) [decodeUntypedInvocation/98 T0xffffff801fe08400 "rootserver" @4012d5]: Untyped Retype: Requested UntypedItem size too s>
main@main.c:49 [Cond failed: error != seL4_NoError]
Failed to retype
这个错误产生的原因是在程序中尝试了创建一个大小为0的untyped。
error = seL4_Untyped_Retype(parent_untyped, // the untyped capability to retype
seL4_UntypedObject, // type
untyped_size_bits, //size
seL4_CapInitThreadCNode, // root
0, // node_index
0, // node_depth
child_untyped, // node_offset
1 // num_caps
);
ZF_LOGF_IF(error != seL4_NoError, "Failed to retype");
练习: 计算子untyped需要的空间大小,以便子untyped能用于创建列表中所有的object。需要注意的是,object的大小是用bit衡量的,2的幂那个。
所以我们看看怎么计算下这个东西的大小:
// TODO work out what size object we need to create to be able to create all of the objects
// listed above. Remember that sizes are in bits, that is, the exponents of powers of two.
seL4_Word untyped_size_bits = 0;
seL4_CPtr parent_untyped = 0;
seL4_CPtr child_untyped = info->empty.start;
我们看这个列表
// list of general seL4 objects
seL4_Word objects[] = {seL4_TCBObject, seL4_EndpointObject, seL4_NotificationObject};
// list of general seL4 object size_bits
seL4_Word sizes[] = {seL4_TCBBits, seL4_EndpointBits, seL4_NotificationBits};
他这里seL4_TCBBits是11,后面两个分别是5和4,显然这两个加起来还不够一个11,我们只需要两个seL4_TCBBits大小的untype就行了。为啥是两个呢,因为实在是没法表示为整数了(话说这个计算方法还真是麻烦)
seL4_Word untyped_size_bits = seL4_TCBBits + 1;
运行成功后,我们看到如下的输出:
<<seL4(CPU 0) [decodeInvocation/645 T0xffffff801fe08400 "rootserver" @4012d5]: Attempted to invoke a null cap #407.>>
main@main.c:57 [Cond failed: error != seL4_NoError]
Failed to set priority
创建一个TCB Object
先来看看错误的原因是啥。
// use the slot after child_untyped for the new TCB cap:
seL4_CPtr child_tcb = child_untyped + 1;
/* TODO create a TCB in CSlot child_tcb */
// try to set the TCB priority
error = seL4_TCB_SetPriority(child_tcb, seL4_CapInitThreadTCB, 10);
ZF_LOGF_IF(error != seL4_NoError, "Failed to set priority");
这里的child_tcb只是一个空的slot,用其设定属性肯定是不行的。
练习: 从child_untyped创建一个TCB object,并且把它的capability放置到child_tcbslot里。
// use the slot after child_untyped for the new TCB cap:
seL4_CPtr child_tcb = child_untyped + 1;
seL4_Untyped_Retype(child_untyped, seL4_TCBObject, 0, seL4_CapInitThreadCNode, 0, 0, child_tcb, 1);
在这里,我们首先是从child_untyped上面来创建于一个seL4_TCBObject类型的对象。我们这个child_untyped是刚才retyped那一块大的untyped得来的。
然后seL4_CapInitThreadCNode和后面的两个参数指定了要把新生成的node放到我们的这个CNode里面,放置的位置是untyped后面一个的slot位置,这个位置就是child_untyped后面的那个空位置。为啥他知道这个是空的,因为刚才child_untyped是这么来的:
seL4_CPtr child_untyped = info->empty.start;
然后我再运行看一下:
main@main.c:65 [Cond failed: cap_id == 0]
Endpoint cap is null cap
嗯,这次属性设定就成功了。我们可以接着往下看了。
创建一个endpoint object
我们来看上面的报错是咋回事。
seL4_CPtr child_ep = child_tcb + 1;
/* TODO create an endpoint in CSlot child_ep */
// identify the type of child_ep
uint32_t cap_id = seL4_DebugCapIdentify(child_ep);
ZF_LOGF_IF(cap_id == 0, "Endpoint cap is null cap");
问题也很简单,这个child_ep也是刚指定一个空的slot,还没retype。
// use the slot after child_tcb for the new endpoint cap:
seL4_CPtr child_ep = child_tcb + 1;
seL4_Untyped_Retype(child_untyped, seL4_EndpointObject, 0, seL4_CapInitThreadCNode, 0, 0, child_ep, 1);
这很容易理解了,不用重复讲了。不过这个retype还挺有意思的,前面创建了一个之后,后面也不用指定起始地址,直接继续用就行了。
这就又往下进行了:
<<seL4(CPU 0) [decodeBindNotification/1635 T0xffffff801fe08400 "rootserver" @4012d5]: TCB BindNotification: Notification is invalid.>>
main@main.c:75 [Cond failed: error != seL4_NoError]
Failed to bind notification.
创建一个notification object
到这里应该就很轻车熟路了:
// use the slot after child_ep for the new notification cap:
seL4_CPtr child_ntfn = child_ep + 1;
// TODO create a notification object in CSlot child_ntfn
// try to use child_ntfn
error = seL4_TCB_BindNotification(child_tcb, child_ntfn);
ZF_LOGF_IF(error != seL4_NoError, "Failed to bind notification.");
照着上面改吧改吧:
// use the slot after child_ep for the new notification cap:
seL4_CPtr child_ntfn = child_ep + 1;
// TODO create a notification object in CSlot child_ntfn
seL4_Untyped_Retype(child_untyped, seL4_NotificationObject, 0, seL4_CapInitThreadCNode, 0, 0, child_ntfn, 1);
// try to use child_ntfn
error = seL4_TCB_BindNotification(child_tcb, child_ntfn);
ZF_LOGF_IF(error != seL4_NoError, "Failed to bind notification.");
这就来到了下一步:
<<seL4(CPU 0) [decodeUntypedInvocation/166 T0xffffff801fe08400 "rootserver" @4012d5]: Untyped Retype: Slot #407 in destination window>
main@main.c:84 [Cond failed: error != seL4_NoError]
Failed to create endpoints.
删除object
// TODO revoke the child untyped
// allocate the whole child_untyped as endpoints
// Remember the sizes are exponents, so this computes 2^untyped_size_bits / 2^seL4_EndpointBits:
seL4_Word num_eps = BIT(untyped_size_bits - seL4_EndpointBits);
error = seL4_Untyped_Retype(child_untyped, seL4_EndpointObject, 0, seL4_CapInitThreadCNode, 0, 0, child_tcb, num_eps);
ZF_LOGF_IF(error != seL4_NoError, "Failed to create endpoints.");
最后一部分代码,是想把整个untyped对象全都创建成seL4_EndpointBits。这肯定是会失败的,因为前面我们已经用了很多对象了。要想这么做需要先把之前的object都删掉。
error = seL4_CNode_Revoke(seL4_CapInitThreadCNode, child_untyped, seL4_WordBits);
assert(error == seL4_NoError);
使用seL4_CNode_Revoke删除所有子object。
再次执行,结果就是成功了:
0x192 0x1fe80000 2^19 untyped
0x193 0x1ff00000 2^19 untyped
0x194 0x1ff80000 2^18 untyped
0x195 0x1ffc0000 2^17 untyped
Success
更多练习
- 在指定的物理地址分配object
- 创建一个简单的对象分配器来分配seL4对象
第二个比较简单,其实就是循环遍历数组,没啥好说的,我觉得需要注意的就是要从大到小遍历。 第一个问题从指定的物理地址分配object,我不知道我理解的是否正确,我们在程序中可以获取到每个untyped的地址:
printf(" CSlot \tPaddr \tSize\tType\n");
for (seL4_CPtr slot = info->untyped.start; slot != info->untyped.end; slot++) {
seL4_UntypedDesc *desc = &info->untypedList[slot - info->untyped.start];
printf("%8p\t%16p\t2^%d\t%s\n", (void *) slot, (void *) desc->paddr, desc->sizeBits, desc->isDevice ? "device untyped" : "untyped");
}
seL4_Error error;
指定物理地址之后我只要遍历这个数组找到对应的地址应该就是可以的了。