Making a New Hypercall && Invoking It from userland via Privcmd

Making a New Hypercall && Invoking an Hypercall from userland via Privcmd

When you want to make a hypercall and invoke it, you should note that Xen refuses the hypercalls which is invoked from userland. So Xen provides /proc/xen/privcmd driver so that developers are able to use when they want to access to the privileged level.

If you want to use privcmd, you should compile dom0 or domU kernel with privileged configurations in the first place. (CONFIG_XEN_PRIVILEGED_GUEST=y)

1. Build an Hypercall in Xen 4.2.0 Kernel

Register a new hypercall

In  xen/arch/x86/x86_64/entry.S , make new lines under ENTRY(hypercall_table) and ENTRY(hypercall_args_table).

ENTRY(hypercall_table)

.quad do_set_trap_tabble
.quad do_mmu_update

.quad do_tmem_op
.quad do_jguno_rdmsr /* new hypercall: it should be placed before “.rept __HYPERVISOR_arch_0…” */
.rept __HYPERVISOR_arch_0-((.-hypercall_table)/8)
.quad do_ni_hypercall
.endr
.quad do_mca
.rept NR_hypercalls-((.-hypercall_table)/8)
.endr 


ENTRY(hypercall_args_table)

.byte 1 /* do_set_trap_table */
.byte 4 /* do_mmu_update */

.byte 1 /* do_tmem_op */
.byte 1 /* do_jguno_rdmsr (This number means the number of arguments) */
.rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
.byte 0 /* do_ni_hypercall */
.endr
.byte 1 /* do_mca */
.rept NR_hypercalls-(.-hypercall_args_table)
.byte 0 /* do_ni_hypercall */
.endr

Define a Constant of New Hypercall

In  xen/include/public/xen.h ,

#define __HYPERVISOR_set_trap_table 0
#define __HYPERVISOR_mmu_update     1

#define __HYPERVISOR_tmem_op        38
#define __HYPERVISOR_jguno_rdmsr     39  // new hypercall

Numbers better be in sequential. If you see a pre-reserved number, then change it (e. g. If you see __HYPERVISOR_xc_reserved_op 39, then change it 40), and make your hypercall number 39.

Declare a Prototype of New Hypercall

In xen/include/xen/hypercall.h,

extern long
do_jguno_rdmsr(
int);

Make a Body of New Hypercall

in xen/common/kernel.c,

DO(jguno_rdmsr)(int input)
{
printk(“cantom hypercall called!\n”);
printk(“input : %d\n”, input);
return 1;
}

2. Use Privcmd Driver Provided by Xen

There is not only one right way to use privcmd. This is just an example, my version.

Define a Do Function Which Declares the Hypercall and Calls It.

There is a file named xc_private.h in tools/libxc folder. Not to make a new file, for convenience, I modified this file.

I Added a new function, next to the function named do_sysctl.

static inline int do_jguno_hypercall(xc_interface *xch, int input_number)
{
int ret;
DECLARE_HYPERCALL;
PERROR(“**do_jguno_hypercall_buffer* START\n”);

hypercall.op = __HYPERVISOR_jguno_rdmsr;
hypercall.arg[0] = input_number;

ret = do_xen_hypercall(xch, &hypercall);

PERROR(“**do_jguno_hypercall_buffer* STOP\n”);
return ret;
}

Using PERROR is just for convenience. It’s a test.

Add a Tool to Use the Do Function

Make a new file named xc_jguno_rdmsr.c in tools/xcutils folder.

#include <err.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>

#include <xenctrl.h>
#include <xenguest.h>
#include <xc_private.h>

int
main(int argc, char **argv)
{
xc_interface *xch;
int ret, input_number;

if ( (argc != 2) )
errx(1, “usage: %s input_number “, argv[0]);

xch = xc_interface_open(0,0,0);
if ( !xch ){
errx(1, “xcutils: xc_jguno_rdmsr.c: failed to open control interface”);
}

input_number= atoi(argv[1]);
printf(“input_number: %d\n”, input_number);

ret = do_jguno_hypercall(xch, input_number);
printf(“return value: %d\n”, ret);

if ( ret == 0 )
{
errx(1, “ret == 0\n”);
fflush(stdout);
}

xc_interface_close(xch);

return ret;
}

And modify the Makefile in the folder.

#
# tools/xcutils/Makefile
#
# This file is subject to the terms and conditions of the GNU General
# Public License. See the file “COPYING” in the main directory of
# this archive for more details.
#
# Copyright (C) 2005 by Christian Limpach
#

XEN_ROOT = $(CURDIR)/../..
include $(XEN_ROOT)/tools/Rules.mk

PROGRAMS = xc_restore xc_save readnotes lsevtchn xc_jguno_rdmsr

CFLAGS += -Werror

CFLAGS_xc_restore.o := $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest)
CFLAGS_xc_save.o := $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest) $(CFLAGS_libxenstore)
CFLAGS_readnotes.o := $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest)
CFLAGS_lsevtchn.o := $(CFLAGS_libxenctrl)
CFLAGS_xc_jguno_rdmsr.o := $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest)

.PHONY: all
all: build

.PHONY: build
build: $(PROGRAMS)

xc_restore: xc_restore.o
$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(APPEND_LDFLAGS)

xc_save: xc_save.o
$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenstore) $(APPEND_LDFLAGS)

readnotes: readnotes.o
$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(APPEND_LDFLAGS)

lsevtchn: lsevtchn.o
$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)

xc_jguno_rdmsr: xc_jguno_rdmsr.o
$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(APPEND_LDFLAGS)

Now compile with “make”. If there’s no error, you can just use it.

3. Use Privcmd Driver Provided by Xen

Just execute the program we just made: xc_jguno_rdmsr

$ xc_jguno_rdmsr 5

Then you are going to see the messages like

input_number: 4
xc: error: **do_jguno_hypercall_buffer* START
(0 = Success): Internal error
xc: error: **do_jguno_hypercall_buffer* STOP
(0 = Success): Internal error
return value: 1

And you can also read printk messages by typing “xl dmesg”.

(XEN) DO(jguno_rdmsr) hypercall called!
(XEN) input : 4

That’s all 🙂