読者です 読者をやめる 読者になる 読者になる

はわわーっ

はわわわわっ

libxenctrlのメモ

使ったのはxen-4.4.1のアーカイブで配布されてるやつ。

libxenctrlをちょっと見てみる。
割り込みのハイパーコールまわりを見たかったのでxc_misc.cのxc_hvm_set_isa_irq_level()をみてみた。

ここから/proc/xen/privcmdにioctlしてカーネルモードに移ってハイパーコールしてるっぽい。

int xc_hvm_set_isa_irq_level(
    xc_interface *xch, domid_t dom,
    uint8_t isa_irq,
    unsigned int level)
{
    DECLARE_HYPERCALL;
    DECLARE_HYPERCALL_BUFFER(struct xen_hvm_set_isa_irq_level, arg);
    int rc;

    arg = xc_hypercall_buffer_alloc(xch, arg, sizeof(*arg));
    if ( arg == NULL )
    {
        PERROR("Could not allocate memory for xc_hvm_set_isa_irq_level hypercall");
        return -1;
    }

    hypercall.op     = __HYPERVISOR_hvm_op;
    hypercall.arg[0] = HVMOP_set_isa_irq_level;
    hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg);

    arg->domid   = dom;
    arg->isa_irq = isa_irq;
    arg->level   = level;

    rc = do_xen_hypercall(xch, &hypercall);

    xc_hypercall_buffer_free(xch, arg);

    return rc;
}

DECLARE_HYPERCALLは

#define DECLARE_HYPERCALL privcmd_hypercall_t hypercall

こうなってて、hypercallっていう変数を定義する。privcmd_hypercall_tは

typedef struct privcmd_hypercall
{
        __u64 op;
        __u64 arg[5];
} privcmd_hypercall_t;

こうなってる。privcmdのオペレーションと引数らしい。

次、DECLARE_HYPERCALL_BUFFER

#define DECLARE_HYPERCALL_BUFFER(_type, _name)                 \
    _type *_name = NULL;                                       \
    xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = { \
        .hbuf = NULL,                                          \
        .param_shadow = NULL,                                  \
        HYPERCALL_BUFFER_INIT_NO_BOUNCE                        \
    }

#define HYPERCALL_BUFFER_INIT_NO_BOUNCE .dir = 0, .sz = 0, .ubuf = (void *)-1

#define XC__HYPERCALL_BUFFER_NAME(_name) xc__hypercall_buffer_##_name

これで(struct xen_hvm_set_isa_irq_level *)型のargとxc_hypercall_buffer_t型のxc__hypercall_buffer_argっていう変数が定義される。
それぞれ構造体は

struct xen_hvm_set_isa_irq_level {
    /* Domain to be updated. */
    domid_t  domid;
    /* ISA device identification, by ISA IRQ (0-15). */
    uint8_t  isa_irq;
    /* Assertion level (0 = unasserted, 1 = asserted). */
    uint8_t  level;
};
typedef struct xen_hvm_set_isa_irq_level xen_hvm_set_isa_irq_level_t;
struct xc_hypercall_buffer {
    /* Hypercall safe memory buffer. */
    void *hbuf;

    /*
     * Reference to xc_hypercall_buffer passed as argument to the
     * current function.
     */
    struct xc_hypercall_buffer *param_shadow;

    /*
     * Direction of copy for bounce buffering.
     */
    int dir;

    /* Used iff dir != 0. */
    void *ubuf;
    size_t sz;
};
typedef struct xc_hypercall_buffer xc_hypercall_buffer_t;

次、xc_hypercall_buffer_alloc

#define xc_hypercall_buffer_alloc(_xch, _name, _size) xc__hypercall_buffer_alloc(_xch, HYPERCALL_BUFFER(_name), _size)

#define HYPERCALL_BUFFER(_name)                                 \
    ({  xc_hypercall_buffer_t _hcbuf_buf1;                      \
        typeof(XC__HYPERCALL_BUFFER_NAME(_name)) *_hcbuf_buf2 = \
                &XC__HYPERCALL_BUFFER_NAME(_name);              \
        (void)(&_hcbuf_buf1 == _hcbuf_buf2);                    \
        (_hcbuf_buf2)->param_shadow ?                           \
                (_hcbuf_buf2)->param_shadow : (_hcbuf_buf2);    \
     })

void *xc__hypercall_buffer_alloc(xc_interface *xch, xc_hypercall_buffer_t *b, size_t size)
{
    size_t actual_size = ROUNDUP(size + sizeof(struct allocation_header), PAGE_SHIFT);
    int nr_pages = actual_size >> PAGE_SHIFT;
    struct allocation_header *hdr;

    hdr = xc__hypercall_buffer_alloc_pages(xch, b, nr_pages);
    if ( hdr == NULL )
        return NULL;
    b->hbuf = (void *)(hdr+1);

    hdr->nr_pages = nr_pages;
    return b->hbuf;
}

ここは何やってるのかよくわからん。
HYPERCALL_BUFFERは結局xc__hypercall_buffer_argのポインタを返すだけなんだろうか。

        (void)(&_hcbuf_buf1 == _hcbuf_buf2);                    \

こことか、これ必要なの?よくわからん。
xc__hypercall_buffer_allocこれは必要なページぶんメモリを確保してxc__hypercall_buffer_arg->hbufを返してる?で合ってるんだろうか。
なので、argというポインタはxc__hypercall_buffer_arg->hbufのポインタと同じになっているはず。

    hypercall.op     = __HYPERVISOR_hvm_op;
    hypercall.arg[0] = HVMOP_set_isa_irq_level;
    hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg);

上2つはいいとして、引数を設定してるところをみる

#define HYPERCALL_BUFFER_AS_ARG(_name)                          \
    ({  xc_hypercall_buffer_t _hcbuf_arg1;                      \
        typeof(XC__HYPERCALL_BUFFER_NAME(_name)) *_hcbuf_arg2 = \
                HYPERCALL_BUFFER(_name);                        \
        (void)(&_hcbuf_arg1 == _hcbuf_arg2);                    \
        (unsigned long)(_hcbuf_arg2)->hbuf;                     \
     })

ここもなにしてるのかよくわからん。

        (void)(&_hcbuf_arg1 == _hcbuf_arg2);                    \

さっきもあったけど、これなんなの。

結局、hypercall.arg[1]にはxc__hypercall_buffer_arg->hbufのポインタ(argのポインタと同じ)をunsigned longにキャストしたものが入ってるっぽい。

で、あとは do_xen_hypercallでハイパーコールまで実行して、後片付けしておしまい。

ということで、最終的にarg[1]はstruct xen_hvm_set_isa_irq_levelへのポインタをunsigned longにキャストしたものになってて
構造体の中身は

    arg->domid   = dom;
    arg->isa_irq = isa_irq;
    arg->level   = level;

でセットしている。

適当にログを出すようにして動かしてみる。

int xc_hvm_set_isa_irq_level(
    xc_interface *xch, domid_t dom,
    uint8_t isa_irq,
    unsigned int level)
{
    DECLARE_HYPERCALL;
    DECLARE_HYPERCALL_BUFFER(struct xen_hvm_set_isa_irq_level, arg);
    int rc;

    arg = xc_hypercall_buffer_alloc(xch, arg, sizeof(*arg));
    if ( arg == NULL )
    {
        PERROR("Could not allocate memory for xc_hvm_set_isa_irq_level hypercall");
        return -1;
    }

    hypercall.op     = __HYPERVISOR_hvm_op;
    hypercall.arg[0] = HVMOP_set_isa_irq_level;
    hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg);

    arg->domid   = dom;
    arg->isa_irq = isa_irq;
    arg->level   = level;

    printf("===== hypercall arg1=%016lx arg1=%016lx domid=%x isa_irq=%x level=%x\n",
            (unsigned long)hypercall.arg[1], (unsigned long)*(__u64 *)hypercall.arg[1],
            dom, isa_irq, level);

    rc = do_xen_hypercall(xch, &hypercall);

    xc_hypercall_buffer_free(xch, arg);

    return rc;
}

こんな感じで出力するようにしたら

===== hypercall arg1=00007f2b3773f004 arg1=0000000000040001 domid=1 isa_irq=4 level=0
===== hypercall arg1=00007f2b3773f004 arg1=0000000001040001 domid=1 isa_irq=4 level=1
===== hypercall arg1=00007f2b3773f004 arg1=0000000000040001 domid=1 isa_irq=4 level=0
===== hypercall arg1=00007f2b3773f004 arg1=0000000000070001 domid=1 isa_irq=7 level=0
===== hypercall arg1=00007f2b3773f004 arg1=0000000001010001 domid=1 isa_irq=1 level=1
===== hypercall arg1=00007f2b3773f004 arg1=00000000000c0001 domid=1 isa_irq=c level=0
===== hypercall arg1=00007f2b3773f004 arg1=0000000000010001 domid=1 isa_irq=1 level=0
===== hypercall arg1=00007f2b3773f004 arg1=00000000000c0001 domid=1 isa_irq=c level=0
===== hypercall arg1=00007f2b3773f004 arg1=0000000000010001 domid=1 isa_irq=1 level=0
===== hypercall arg1=00007f2b3773f004 arg1=00000000000c0001 domid=1 isa_irq=c level=0

こうなった。

アドレスはいつも同じところ使ってるらしい。
中身はdomidとisa_irqとlevelが順番に入ったものになっている。