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.mkPROGRAMS = 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 🙂