view paper/cbc_interface.tex @ 12:9cf9e0b086c7

fix private code
author tobaru
date Tue, 04 Feb 2020 15:35:28 +0900
parents f7ed2b4874f4
children 1ef114182e80
line wrap: on
line source

\chapter{CbC インターフェース}

構造図書く(今のcbcxv6と同じか確認してから)
\par


 Gears OS では Meta Code Gear で Context から値を取り出し、ノーマルレベルの Code Gear に値を渡す。
しかし、Code Gaer がどの Data Gear の番号に対応するかを指定する必要があったり、 % ぱるすさんコード必要?
ノーマルレベルとメタレベルで見え方が異なる Data Gear を Meta Code Gear によって 調整する必要があったりと、 % みつきさん
メタレベルからノーマルレベルの継続の記述が煩雑になるため、Interface 化をしている。
Interface は Data Gear に対しての操作を行う Code Gear であり、実装は別で定義する。
% Interface で定義した Code Gear に


%  Xv6 の書き換えは Interface を用いてモジュール化する。
そうすることで Gears OS の機能を置き換えることできるようになる。



\section{インターフェースの定義}
 インターフェースはある Data Gear の定義と、
それに対する操作を行う Code Gear の集合を表現する Meta Data Gear である。
Context では全ての Code Gaer と Data Gear の集合を表現していることに対し、
インターフェースは一部の Code Gear と一部の Data Gear の集合を表現する。
\par 
インターフェースを記述することによってノーマルレベルとメタレベルの分離が可能となる。

Paging のインターフェースを記述したコードを ソースコード \ref{interface} に示す。


\begin{lstlisting}[frame=lrbt,label=interface,caption={\footnotesize vm のインターフェースの定義(vm.h)}]
typedef struct vm<Type,Impl> {
    union Data* vm;
    uint low;
    uint hi;
    struct proc* p;
    pde_t* pgdir;
    char* init;
    uint sz;
    char* addr;
    struct inode* ip;
    uint offset;
    uint oldsz;
    uint newsz;
    char* uva;
    uint va;
    void* pp;
    uint len;
    uint phy_low;
    uint phy_hi;
    __code init_vmm(Impl* vm, __code next(...));
    __code kpt_freerange(Impl* vm, uint low, uint hi, __code next(...));
    __code kpt_alloc(Impl* vm ,__code next(...));
    __code switchuvm(Impl* vm ,struct proc* p, __code next(...));
    __code init_inituvm(Impl* vm, pde_t* pgdir, char* init, uint sz, __code next(...));
    __code loaduvm(Impl* vm,pde_t* pgdir, char* addr, struct inode* ip, uint offset, uint sz,  __code next(...));
    __code allocuvm(Impl* vm, pde_t* pgdir, uint oldsz, uint newsz, __code next(...));
    __code clearpteu(Impl* vm, pde_t* pgdir, char* uva,  __code next(...));
    __code copyuvm(Impl* vm, pde_t* pgdir, uint sz, __code next(...));
    __code uva2ka(Impl* vm, pde_t* pgdir, char* uva, __code next(...));
    __code copyout(Impl* vm, pde_t* pgdir, uint va, void* pp, uint len, __code next(...));
    __code paging_int(Impl* vm, uint phy_low, uint phy_hi, __code next(...));
    __code void_ret(Impl* vm);
    __code next(...);
} vm;
\end{lstlisting}

1行目ので実装名を定義している。
typedef struct の直後に実装名(vm)を書く。
\par
2行目から19行目で引数の Data Gear 郡を定義している。
初期化された Data Gear がそれぞれの Code Gear の引数として扱われる。
例として、2行目で定義された vm が21行目から32行目までの引数と対応している。
\par
% インターフェースの Code Gear の goto による継続先は基本的に不定となっており、継続元から渡される。
Code Gear は\_\_code CodeGearName () で記述する。
第一引数である Impl* vm が Code Gear の型になる。
\par
\_\_code next(...) の引数 ... は複数の Input Data Gear を持つという意味である。
後述する実装によって条件分岐によって複数の継続先が設定されることがある。
\par
 Code Gaer は 20行目から33行目のように "\_\_code [Code Gear名]([引数])"で定義する。
この引数が input Data Gear になる。


% 実装側に書く
% 引数の Data Gear はその Code Gear の Input Data Gear になり、引数の Code Gear の中の引 数が Output Data Gear になる。Code Gear の第一引数には Interface を実装した Data Gear を渡す。これは、Code Gear の操作の対象となる Data Gear を設定し ており、後述する継続構文では引数として記述を行わない。


\section{インターフェースの実装}
 インターフェースは Data Gear に対しての Code Gear とその Code Gear で扱われている Data Gear の集合を抽象化した Meta Data Gear で、vm.c に対応する実装は別で定義する。
\par
インターフェースの実装についてソースコード \ref{impl_vm} で示す。


\begin{lstlisting}[frame=lrbt,label=impl_vm,caption={\footnotesize vm インターフェースの実装}]
#include "../../context.h"
#interface "vm.h"

vm* createvm_impl(struct Context* cbc_context) {
    struct vm* vm  = new vm();
    struct vm_impl* vm_impl = new vm_impl();
    vm->vm = (union Data*)vm_impl;
    vm_impl->vm_impl = NULL;
    vm_impl->i  = 0;
    vm_impl->pte = NULL;
    vm_impl->sz  = 0;
    vm_impl->loaduvm_ptesize_check = C_loaduvm_ptesize_checkvm_impl;
    vm_impl->loaduvm_loop = C_loaduvm_loopvm_impl;
    vm_impl->allocuvm_check_newsz = C_allocuvm_check_newszvm_impl;
    vm_impl->allocuvm_loop = C_allocuvm_loopvm_impl;
    vm_impl->copyuvm_check_null = C_copyuvm_check_nullvm_impl;
    vm_impl->copyuvm_loop = C_copyuvm_loopvm_impl;
    vm_impl->uva2ka_check_pe_types = C_uva2ka_check_pe_types;
    vm_impl->paging_intvm_impl = C_paging_intvmvm_impl;
    vm_impl->copyout_loopvm_impl = C_copyout_loopvm_impl;
    vm_impl->switchuvm_check_pgdirvm_impl = C_switchuvm_check_pgdirvm_impl;
    vm_impl->init_inituvm_check_sz = C_init_inituvm_check_sz;
    vm->void_ret  = C_vm_void_ret;
    vm->init_vmm = C_init_vmmvm_impl;
    vm->kpt_freerange = C_kpt_freerangevm_impl;
    vm->kpt_alloc = C_kpt_allocvm_impl;
    vm->switchuvm = C_switchuvmvm_impl;
    vm->init_inituvm = C_init_inituvmvm_impl;
    vm->loaduvm = C_loaduvmvm_impl;
    vm->allocuvm = C_allocuvmvm_impl;
    vm->clearpteu = C_clearpteuvm_impl;
    vm->copyuvm = C_copyuvmvm_impl;
    vm->uva2ka = C_uva2kavm_impl;
    vm->copyout = C_copyoutvm_impl;
    vm->paging_int = C_paging_intvm_impl;
    return vm;
}
extern struct {
    struct spinlock lock;
    struct run *freelist;
} kpt_mem;

__code init_vmmvm_impl(struct vm_impl* vm,__code next(...)) {
    initlock(&kpt_mem.lock, "vm");
    kpt_mem.freelist = NULL;

    goto next(...);
}

extern struct run {
    struct run *next;
};

static void _kpt_free (char *v)
{   
    struct run *r;
    
    r = (struct run*) v;
    r->next = kpt_mem.freelist;
    kpt_mem.freelist = r;
}

__code kpt_freerangevm_impl(struct vm_impl* vm, uint low, uint hi, __code next(...)) {

   if (low < hi) {
     _kpt_free((char*)low);
     goto kpt_freerangevm_impl(vm, low + PT_SZ, hi, next(...));

   }
  goto next(...);
}

__code kpt_allocvm_impl(struct vm_impl* vm, __code next(...)) {
  acquire(&kpt_mem.lock);

  goto kpt_alloc_check_impl(vm_impl, next(...));
}

typedef struct proc proc;
__code switchuvmvm_impl(struct vm_impl* vm , struct proc* p, __code next(...)) { //:skip

    goto switchuvm_check_pgdirvm_impl(...);
}

__code init_inituvmvm_impl(struct vm_impl* vm, pde_t* pgdir, char* init, uint sz, __code next(...)) {

    Gearef(cbc_context, vm_impl)->pgdir = pgdir;
    Gearef(cbc_context, vm_impl)->init = init;
    Gearef(cbc_context, vm_impl)->sz = sz;
    Gearef(cbc_context, vm_impl)->next = next;
    goto init_inituvm_check_sz(vm, pgdir, init, sz, next(...));
}

__code loaduvmvm_impl(struct vm_impl* vm, pde_t* pgdir, char* addr, struct inode* ip, uint offset, uint sz,  __code next(...)) {
    Gearef(cbc_context, vm_impl)->pgdir = pgdir;
    Gearef(cbc_context, vm_impl)->addr = addr;
    Gearef(cbc_context, vm_impl)->ip = ip;
    Gearef(cbc_context, vm_impl)->offset = offset;
    Gearef(cbc_context, vm_impl)->sz = sz;
    Gearef(cbc_context, vm_impl)->next = next;

    goto loaduvm_ptesize_checkvm_impl(vm, next(...));
}

__code allocuvmvm_impl(struct vm_impl* vm, pde_t* pgdir, uint oldsz, uint newsz, __code next(...)) {

    goto allocuvm_check_newszvm_impl(vm, pgdir, oldsz, newsz, next(...));
}

__code clearpteuvm_impl(struct vm_impl* vm, pde_t* pgdir, char* uva,  __code next(...)) {

    goto clearpteu_check_ptevm_impl(vm, pgdir, uva, next(...));
}

__code copyuvmvm_impl(struct vm_impl* vm, pde_t* pgdir, uint sz, __code next(...)) {

    goto copyuvm_check_nullvm_impl(vm, pgdir, sz, __code next(...));
}

__code uva2kavm_impl(struct vm_impl* vm, pde_t* pgdir, char* uva, __code next(...)) {

    goto uva2ka_check_pe_types(vm, pgdir, uva, next(...));
}

__code copyoutvm_impl(struct vm_impl* vm, pde_t* pgdir, uint va, void* pp, uint len, __code next(...)) {

    vm->buf = (char*) pp;

    goto copyout_loopvm_impl(vm, pgdir, va, pp, len, va0, pa0, next(...));
}

__code paging_intvm_impl(struct vm_impl* vm, uint phy_low, uint phy_hi, __code next(...)) {

    goto paging_intvmvm_impl(vm, phy_low, phy_hi, next(...));
}

__code vm_void_ret(struct vm_impl* vm) {
    return;
}

\end{lstlisting}


2行目のようにインターフェースのヘッダーファイルは \#interface で呼び出す。
\par
create\_impl の関数内で、インターフェースを vm で定義し、23行目の vm-$>$void\_ret のようにそれぞれのインターフェースに対応させていく。
\par
CbCは1つ1つの関数の信頼性を保障させるために細かくする必要があるので、for文やif文がある場合はさらに実装を分ける。vm と同じように vm\_impl を定義し、遷移する関数名に対応させていく。分けた実装はさらに別で実装する(vm\_impl\_private.cbc)。


% インターフェースを呼び出す場合は、struct vm* vm = createvm_impl(cbc_context); 
\par








\section{インターフェース内の private メソッド}
インターフェースで定義した Code Gear 以外の Code Gaer も記述することができる。
この Code Gear は基本的にインターフェースで指定された Code Gear 内からのみ継続されるため、
Java の private メソッドのように扱われる。
\par
インターフェースと同じようにヘッダーファイルをソースコード \ref{impl_vm_privateh} で定義する。


\begin{lstlisting}[frame=lrbt,label=impl_vm_privateh,caption={\footnotesize vm private のヘッダーファイル}]
typedef struct vm_impl<Impl, Isa> impl vm{
    union Data* vm_impl;
    uint i;
    pte_t* pte;
    uint sz;
    pde_t* pgdir;
    char* addr;
    struct inode* ip;
    uint offset;
    uint pa;
    uint n;
    uint oldsz;
    uint newsz;
    uint a;
    int ret;
    char* mem;
    char* uva;
    pde_t* d;
    uint ap;
    uint phy_low;
    uint phy_hi;
    uint va;
    void* pp;
    uint len;
    char* buf;
    char* pa0;
    uint va0;
    proc_struct* p;
    char* init;

    __code kpt_alloc_check_impl(Type* vm_impl, __code next(...));
    __code loaduvm_ptesize_check(Type* vm_impl, __code next(int ret, ...));
    __code loaduvm_loop(Type* vm_impl, uint i, pte_t* pte, uint sz, __code next(int ret, ...));
    __code allocuvm_check_newsz(Type* vm_impl, pde_t* pgdir, uint oldsz, uint newsz, __code next(...));
    __code allocuvm_loop(Type* vm_impl, pde_t* pgdir, uint oldsz, uint newsz, uint a, __code next(...));
    __code copyuvm_check_null(Type* vm_impl, pde_t* pgdir, uint sz, __code next(...));
    __code copyuvm_loop(Type* vm_impl,pde_t* pgdir, uint sz, pde_t* d, pte_t* pte, uint pa, uint i, uint ap, char* mem, __code next(int ret, ...));
    __code clearpteu_check_ptevm_impl(Type* vm_impl, pde_t* pgdir, char* uva,  __code next(...));
    __code uva2ka_check_pe_types(Type* vm_impl, pde_t* pgdir, char* uva, __code next(...));
    __code paging_intvm_impl(Type* vm_impl, uint phy_low, uint phy_hi, __code next(...));
    __code copyout_loopvm_impl(Type* vm_impl, pde_t* pgdir, uint va, void* pp, uint len, __code next(...));
    __code switchuvm_check_pgdirvm_impl(struct vm_impl* vm_impl, struct proc* p, __code next(...));
    __code init_inituvm_check_sz(struct vm_impl* vm_impl, pde_t* pgdir, char* init, uint sz, __code next(...));
    __code void_ret(Type* vm_impl);
    __code next(...);
} vm_impl;

\end{lstlisting}



private での CbC の記述を vm.c と比べて説明する。
全体の記述量が多いため、if文とfor文のある loaduvm という関数で説明を行う。


\begin{lstlisting}[frame=lrbt,label=vm_loaduvm,caption={\footnotesize vm.c のloaduvm}]

int loaduvm (pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
{
    uint i, pa, n;
    pte_t *pte;

    if ((uint) addr % PTE_SZ != 0) {
        panic("loaduvm: addr must be page aligned");
    }

    for (i = 0; i < sz; i += PTE_SZ) {
        if ((pte = walkpgdir(pgdir, addr + i, 0)) == 0) {
            panic("loaduvm: address should exist");
        }

        pa = PTE_ADDR(*pte);

        if (sz - i < PTE_SZ) {
            n = sz - i;
        } else {
            n = PTE_SZ;
        }

        if (readi(ip, p2v(pa), offset + i, n) != n) {
            return -1;
        }
    }

    return 0;
}

\end{lstlisting}




\begin{lstlisting}[frame=lrbt,label=impl_vm_loaduvm,caption={\footnotesize privateでの loaduvm の実装}]
#interface "vm_impl.h"

__code loaduvm_ptesize_checkvm_impl(struct vm_impl* vm_impl, __code next(int ret, ...)) {
    char* addr = vm_impl->addr;

    if ((uint) addr %PTE_SZ != 0) {
       // goto panic
    }

    goto loaduvm_loopvm_impl(vm_impl, next(ret, ...));
}

__code loaduvm_loopvm_impl(struct vm_impl* vm_impl, __code next(int ret, ...)) {
    uint i = vm_impl->i;
    uint sz = vm_impl->sz;

    if (i < sz) {
        goto loaduvm_check_pgdir(vm_impl, next(ret, ...));
    }

    goto loaduvm_exit(vm_impl, next(ret, ...));
}


static pte_t* walkpgdir (pde_t *pgdir, const void *va, int alloc)
{   
    pde_t *pde;
    pte_t *pgtab;
    
    // pgdir points to the page directory, get the page direcotry entry (pde)
    pde = &pgdir[PDE_IDX(va)];
    
    if (*pde & PE_TYPES) {
        pgtab = (pte_t*) p2v(PT_ADDR(*pde));
    
    } else {
        if (!alloc || (pgtab = (pte_t*) kpt_alloc()) == 0) {
            return 0;
        }
        
        // Make sure all those PTE_P bits are zero.
        memset(pgtab, 0, PT_SZ);
        
        // The permissions here are overly generous, but they can
        // be further restricted by the permissions in the page table
        // entries, if necessary.
        *pde = v2p(pgtab) | UPDE_TYPE;
    }

    return &pgtab[PTE_IDX(va)];
}


__code loaduvm_check_pgdir(struct vm_impl* vm_impl, __code next(int ret, ...)) {
    pte_t* pte = vm_impl->pte;
    pde_t* pgdir = vm_impl->pgdir;
    uint i = vm_impl->i;
    char* addr = vm_impl->addr;
    uint pa = vm_impl->pa;

    if ((pte = walkpgdir(pgdir, addr + i, 0)) == 0) {
        // goto panic
    }
    pa = PTE_ADDR(*pte);

    vm_impl->pte = pte;
    vm_impl->pgdir = pgdir;
    vm_impl->addr = addr;
    vm_impl->pa = pa;

    goto loaduvm_check_PTE_SZ(vm_impl, next(ret, ...));
}

__code loaduvm_check_PTE_SZ(struct vm_impl* vm_impl, __code next(int ret, ...)) {
    uint sz = vm_impl->sz;
    uint i = vm_impl->i;
    uint n = vm_impl->n;
    struct inode* ip = vm_impl->ip;
    uint pa = vm_impl->pa;
    uint offset = vm_impl->offset;

    if (sz - i < PTE_SZ) {
        n = sz - i;
    } else {
        n = PTE_SZ;
    }

    if (readi(ip, p2v(pa), offset + i, n) != n) {
        ret = -1;
        goto next(ret, ...);
    }

    vm_impl->n = n;

    goto loaduvm_loopvm_impl(vm_impl, next(ret, ...));
}

__code loaduvm_exit(struct vm_impl* vm_impl, __code next(int ret, ...)) {
    ret = 0;
    goto next(ret, ...);
}

\end{lstlisting}





\section{インターフェースの呼び出し}
定義したインターフェースの呼び出し方について説明する。
 CbC の場合 goto による遷移を行うので、関数呼び出しのように goto 以降のコードを実行できない。
例として、ソースコード \ref{cbc_goto} の16行目のように goto によってインターフェースで定義した命令を行うと、戻ってこれないため17行目以降が実行されなくなる。


\begin{lstlisting}[frame=lrbt,label=cbc_goto,caption={\footnotesize cbc インターフェースのgoto}]

void userinit(void)
{
    struct proc* p;
    extern char _binary_initcode_start[], _binary_initcode_size[];

    p = allocproc();
    initContext(&p->cbc_context);

    initproc = p;

    if((p->pgdir = kpt_alloc()) == NULL) {
        panic("userinit: out of memory?");
    }

    goto cbc_init_vmm_dummy(&p->cbc_context, p, p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);
    p->sz = PTE_SZ;

    // craft the trapframe as if
    memset(p->tf, 0, sizeof(*p->tf));


\end{lstlisting}




\begin{lstlisting}[frame=lrbt,label=dummy,caption={\footnotesize dummy を使った呼び出し}]


void dummy(struct proc *p, char _binary_initcode_start[], char _binary_initcode_size[])
{
    // inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);
    goto cbc_init_vmm_dummy(&p->cbc_context, p, p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);

}



__ncode cbc_init_vmm_dummy(struct Context* cbc_context, struct proc* p, pde_t* pgdir, char* init, uint sz){//:skip

    struct vm* vm = createvm_impl(cbc_context);
    // goto vm->init_vmm(vm, pgdir, init, sz , vm->void_ret);
        Gearef(cbc_context, vm)->vm = (union Data*) vm;
        Gearef(cbc_context, vm)->pgdir = pgdir;
        Gearef(cbc_context, vm)->init = init;
        Gearef(cbc_context, vm)->sz = sz ;
        Gearef(cbc_context, vm)->next = C_vm_void_ret ;
    goto meta(cbc_context, vm->init_inituvm);
}


void userinit(void)
{
    struct proc* p;
    extern char _binary_initcode_start[], _binary_initcode_size[];

    p = allocproc();
    initContext(&p->cbc_context);

    initproc = p;

    if((p->pgdir = kpt_alloc()) == NULL) {
        panic("userinit: out of memory?");
    }

    dummy(p, _binary_initcode_start, _binary_initcode_size);



\end{lstlisting}
ソースコードの説明