L4:map pages from sigma0


客户端请求

在root task中通常会先将地址空间的所有页面从sigma0中分配出来,这样以后创建的thread运行时,若没有指定pager,仍然可以正常运行。
具体的请求页面操作如下所示:
        // ** pin all code and data of the root task by requesting the pages from sigma0
        for (const char
addr = &elf_start; addr < &elf_end; addr += pagesize)
        {
                if (!request_page((L4_Word_t)addr)) {
                        panic("root: could not get roottask pages from sigma0.");
                }
        }
当然,也可以通过系统自身的缺页机制:
        // Touch the memory to make sure we never get pagefaults
        extern L4_Word_t _end, _start;
        for (L4_Word_t x = (&_start); x < &_end; x++)
        {
            volatile L4_Word_t q;
            q =
(volatile L4_Word_t) x;
        }


static inline L4_Bool_t request_page(L4_Word_t addr)
{      
    return !(L4_IsNilFpage( L4_Sigma0_GetPage(sigma0id, L4_Fpage(addr, pagesize)) ) );
}              
   

request_page会调用 L4_Sigma0_GetPage(sigma0id, L4_Fpage(addr, pagesize)) ),向sigma0请求起始地址为addr,大小为pagesize的页面。


L4_INLINE L4_Fpage_t L4_Sigma0_GetPage (L4_ThreadId_t s0, L4_Fpage_t f)
{
    return L4_Sigma0_GetPage_RcvWindow (s0, f, L4_CompleteAddressSpace);
}

L4_Sigma0_GetPage_RcvWindow()指定receive windows为L4_CompleteAddressSpace,即当前线程的全部地址空间,向sigma0发送并等待ipc消息:
L4_INLINE L4_Fpage_t L4_Sigma0_GetPage_RcvWindow (L4_ThreadId_t s0,
                                                  L4_Fpage_t f,
                                                  L4_Fpage_t RcvWindow)
{
    L4_MsgTag_t tag;
    L4_Msg_t msg;

    if (L4_IsNilThread (s0))
    {
        L4_KernelInterfacePage_t
kip = (L4_KernelInterfacePage_t )
            L4_GetKernelInterface ();
        s0 = L4_GlobalId (kip->ThreadInfo.X.UserBase, 1);
    }

    L4_MsgClear (&msg);
    L4_MsgAppendWord (&msg, f.raw);
    L4_MsgAppendWord (&msg, 0);
    L4_Set_MsgLabel (&msg, (L4_Word_t) -6UL << 4);
    L4_MsgLoad (&msg);
    L4_Accept (L4_MapGrantItems (RcvWindow));
       
    tag = L4_Call (s0);
    if (L4_IpcFailed (tag))
        return L4_Nilpage;
       
    L4_StoreMR (2, &f.raw);
    return f;
}

L4_MapGrantItems() 返回一个类型为 L4_Acceptor_t 的 acceptor,它指定了Receive Windows的大小
L4_Accept()把fpage结构体写入到BR0(Buffer Register 0)中
参见L4_User_Manual-x24.4. SHARING MEMORY — MEMORY MAPPING
关于GrantItem参见“L4 eXperimental Kernel Reference Manual (Version X.2)
5.3 GrantItem”

当L4_Call()返回时,内核已经完成了page的映射(可以理解为设置好了page table中的项)。



服务端响应

对应的服务端代码在serv/sigma0/sigma0.cc中:
extern "C" void sigma0_main (void)
{
    …

    for(;;)
    {
        case L4_SIGMA0_RPC:
        {
            fpage.raw = L4_Get (&msg, 0);
            L4_Word_t addr = L4_Address (fpage);
            L4_Word_t attributes = L4_Get (&msg, 1);
            …
            allocate_page (tid, addr, L4_SizeLog2 (fpage), map));
            …
         }



        L4_Put (&msg, 0, 0, (L4_Word_t
) 0, 2, &map);
        L4_Load (&msg);
        tag = L4_ReplyWait (tid, &tid);
    }
}

sigma0_main()如果接受到了L4_SIGMA0_RPC ipc,会分配page:
bool allocate_page (L4_ThreadId_t tid, L4_Word_t addr, L4_Word_t log2size,
                    L4_MapItem_t & map, bool only_conventional)
{
    memregion_t * r;
    L4_Fpage_t fp;

    …

    // Try allocating from one of the memory pools.
    for (L4_Word_t i = 0; pools[i] != NULL; i++)
    {
        pools[i]->reset ();
        while ((r = pools[i]->next ()) != NULL)
        {
            if (r->low > addr_high || r->high < addr)
                continue;

            fp = r->allocate (addr, log2size);
            if (! L4_IsNilFpage (fp))
            {
                map = L4_MapItem (fp, addr);
                alloc_pool.insert
                    (new memregion_t (addr, addr_high, tid));
                return true;
            }
        }
    }

    …

}

其中的map = L4_MapItem(fp, addr)返回类型为L4_MapItem_t,它指定返回页面将被映射到请求线程的起始地址为addr的地方,参见L4_User_Manual-x2____4.4. SHARING MEMORY — MEMORY MAPPING

sigma0发送响应ipc后,内核会将所有的映射工作做好。









end