Update kvm-unit-test from upstream

This commit is contained in:
Fabian 2017-07-27 16:05:06 +02:00
parent cda9398da5
commit 1b6c1091ce
65 changed files with 3718 additions and 889 deletions

View file

@ -17,3 +17,6 @@ cscope.*
/build-head
/logs/
/logs.old/
/api/api-sample
/api/dirty-log
/api/dirty-log-perf

View file

@ -1,4 +1,10 @@
Copyright (C) 2006 Qumranet.
Copyright (C) 2007-2017 by various contributors (see source files for details)
The files in this directory and its subdirectories are licensed under the
GNU LGPL, version 2.
The kvm-unit-tests are free software; the whole package can be redistributed
and/or modified under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
Many files in this directory and its subdirectories are also licensed under
the less restrictive GNU LGPL, version 2, or other compatible licenses. See
the individual files for details.

View file

@ -76,6 +76,13 @@ F: powerpc/*
F: lib/powerpc/*
F: lib/ppc64/*
S390X
M: Thomas Huth <thuth@redhat.com>
M: David Hildenbrand <david@redhat.com>
L: kvm@vger.kernel.org
F: s390x/*
F: lib/s390x/*
X86
M: Paolo Bonzini <pbonzini@redhat.com>
M: Radim Krčmář <rkrcmar@redhat.com>

View file

@ -1,5 +1,5 @@
SHELL := /bin/bash
SHELL := /usr/bin/env bash
ifeq ($(wildcard config.mak),)
$(error run ./configure first. See ./configure -h)
@ -7,8 +7,12 @@ endif
include config.mak
# Set search path for all sources
VPATH = $(SRCDIR)
libdirs-get = $(shell [ -d "lib/$(1)" ] && echo "lib/$(1) lib/$(1)/asm")
ARCH_LIBDIRS := $(call libdirs-get,$(ARCH)) $(call libdirs-get,$(TEST_DIR))
OBJDIRS := $(ARCH_LIBDIRS)
DESTDIR := $(PREFIX)/share/kvm-unit-tests/
@ -30,13 +34,15 @@ cflatobjs := \
# libfdt paths
LIBFDT_objdir = lib/libfdt
LIBFDT_srcdir = lib/libfdt
LIBFDT_srcdir = $(SRCDIR)/lib/libfdt
LIBFDT_archive = $(LIBFDT_objdir)/libfdt.a
LIBFDT_include = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_INCLUDES))
LIBFDT_version = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_VERSION))
#include architecure specific make rules
include $(TEST_DIR)/Makefile
OBJDIRS += $(LIBFDT_objdir)
#include architecture specific make rules
include $(SRCDIR)/$(TEST_DIR)/Makefile
# cc-option
# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
@ -44,8 +50,10 @@ include $(TEST_DIR)/Makefile
cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
> /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
CFLAGS += -g
CFLAGS += $(autodepend-flags) -Wall -Werror
COMMON_CFLAGS += -g $(autodepend-flags)
COMMON_CFLAGS += -Wall -Wwrite-strings -Wclobbered -Wempty-body -Wuninitialized
COMMON_CFLAGS += -Wignored-qualifiers -Wunused-but-set-parameter
COMMON_CFLAGS += -Werror
frame-pointer-flag=-f$(if $(KEEP_FRAME_POINTER),no-,)omit-frame-pointer
fomit_frame_pointer := $(call cc-option, $(frame-pointer-flag), "")
fnostack_protector := $(call cc-option, -fno-stack-protector, "")
@ -53,34 +61,42 @@ fnostack_protector_all := $(call cc-option, -fno-stack-protector-all, "")
wno_frame_address := $(call cc-option, -Wno-frame-address, "")
fno_pic := $(call cc-option, -fno-pic, "")
no_pie := $(call cc-option, -no-pie, "")
CFLAGS += $(fomit_frame_pointer)
CFLAGS += $(fno_stack_protector)
CFLAGS += $(fno_stack_protector_all)
CFLAGS += $(wno_frame_address)
CFLAGS += $(if $(U32_LONG_FMT),-D__U32_LONG_FMT__,)
CFLAGS += $(fno_pic) $(no_pie)
COMMON_CFLAGS += $(fomit_frame_pointer)
COMMON_CFLAGS += $(fno_stack_protector)
COMMON_CFLAGS += $(fno_stack_protector_all)
COMMON_CFLAGS += $(wno_frame_address)
COMMON_CFLAGS += $(if $(U32_LONG_FMT),-D__U32_LONG_FMT__,)
COMMON_CFLAGS += $(fno_pic) $(no_pie)
CXXFLAGS += $(CFLAGS)
CFLAGS += $(COMMON_CFLAGS)
CFLAGS += -Wmissing-parameter-type -Wold-style-declaration -Woverride-init
CXXFLAGS += $(COMMON_CFLAGS)
autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d
LDFLAGS += $(CFLAGS)
LDFLAGS += -pthread -lrt
$(libcflat): $(cflatobjs)
$(AR) rcs $@ $^
include $(LIBFDT_srcdir)/Makefile.libfdt
$(LIBFDT_archive): CFLAGS += -ffreestanding -I lib -I lib/libfdt -Wno-sign-compare
$(LIBFDT_archive): CFLAGS += -ffreestanding -I $(SRCDIR)/lib -I $(SRCDIR)/lib/libfdt -Wno-sign-compare
$(LIBFDT_archive): $(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS))
$(AR) rcs $@ $^
# Build directory target
.PHONY: directories
directories:
@mkdir -p $(OBJDIRS)
%.o: %.S
$(CC) $(CFLAGS) -c -nostdlib -o $@ $<
-include */.*.d */*/.*.d
all: $(shell git rev-parse --verify --short=8 HEAD >build-head 2>/dev/null)
all: directories $(shell cd $(SRCDIR) && git rev-parse --verify --short=8 HEAD >$(PWD)/build-head 2>/dev/null)
standalone: all
@scripts/mkstandalone.sh

View file

@ -1,17 +1,20 @@
#!/bin/bash
#!/usr/bin/env bash
srcdir=$(cd "$(dirname "$0")"; pwd)
prefix=/usr/local
cc=gcc
cxx=g++
ld=ld
objcopy=objcopy
objdump=objdump
ar=ar
addr2line=addr2line
arch=i386
arch=`uname -m | sed -e 's/i.86/i386/;s/arm.*/arm/;s/ppc64.*/ppc64/'`
host=$arch
cross_prefix=
endian=""
pretty_print_stacks=yes
environ_default=yes
u32_long=
usage() {
@ -23,11 +26,15 @@ usage() {
--processor=PROCESSOR processor to compile for ($arch)
--cross-prefix=PREFIX cross compiler prefix
--cc=CC c compiler to use ($cc)
--cxx=CXX c++ compiler to use ($cxx)
--ld=LD ld linker to use ($ld)
--prefix=PREFIX where to install things ($prefix)
--endian=ENDIAN endianness to compile for (little or big, ppc64 only)
--[enable|disable]-pretty-print-stacks
enable or disable pretty stack printing (enabled by default)
--[enable|disable]-default-environ
enable or disable the generation of a default environ when
no environ is provided by the user (enabled by default)
EOF
exit 1
}
@ -58,6 +65,9 @@ while [[ "$1" = -* ]]; do
--cc)
cc="$arg"
;;
--cxx)
cxx="$arg"
;;
--ld)
ld="$arg"
;;
@ -67,6 +77,12 @@ while [[ "$1" = -* ]]; do
--disable-pretty-print-stacks)
pretty_print_stacks=no
;;
--enable-default-environ)
environ_default=yes
;;
--disable-default-environ)
environ_default=no
;;
--help)
usage
;;
@ -102,12 +118,12 @@ elif [ "$arch" = "ppc64" ]; then
else
testdir=$arch
fi
if [ ! -d $testdir ]; then
if [ ! -d "$srcdir/$testdir" ]; then
echo "$testdir does not exist!"
exit 1
fi
if [ -f $testdir/run ]; then
ln -fs $testdir/run $testdir-run
if [ -f "$srcdir/$testdir/run" ]; then
ln -fs "$srcdir/$testdir/run" $testdir-run
fi
# check if uint32_t needs a long format modifier
@ -117,42 +133,56 @@ EOF
u32_long=$($cross_prefix$cc -E lib-test.c | grep -v '^#' | grep -q long && echo yes)
rm -f lib-test.c
# check for dependent 32 bit libraries
if [ "$arch" != "arm" ]; then
cat << EOF > lib_test.c
#include <stdc++.h>
#include <boost_thread-mt.h>
#include <pthread.h>
int main ()
{}
EOF
$cc -m32 -o /dev/null lib_test.c &> /dev/null
exit=$?
if [ $exit -eq 0 ]; then
api=true
# api/: check for dependent 32 bit libraries and gnu++11 support
if [ "$testdir" = "x86" ]; then
echo 'int main () {}' > lib-test.c
$cc -m32 -o /dev/null -lstdc++ -lpthread -lrt lib-test.c &> /dev/null
exit=$?
$cxx -m32 -o /dev/null -std=gnu++11 lib-test.c &> /dev/null
if [ $? -eq 0 -a $exit -eq 0 ]; then
api=true
fi
rm -f lib-test.c
fi
rm -f lib_test.c
# Are we in a separate build tree? If so, link the Makefile
# and shared stuff so that 'make' and run_tests.sh work.
if test ! -e Makefile; then
echo "linking Makefile..."
ln -s "$srcdir/Makefile" .
echo "linking tests..."
mkdir -p $testdir
ln -sf "$srcdir/$testdir/run" $testdir/
ln -sf "$srcdir/$testdir/unittests.cfg" $testdir/
ln -sf "$srcdir/run_tests.sh"
echo "linking scripts..."
ln -sf "$srcdir/scripts"
fi
# link lib/asm for the architecture
rm -f lib/asm
asm=asm-generic
if [ -d lib/$arch/asm ]; then
asm=$arch/asm
elif [ -d lib/$testdir/asm ]; then
asm=$testdir/asm
if [ -d "$srcdir/lib/$arch/asm" ]; then
asm="$srcdir/lib/$arch/asm"
elif [ -d "$srcdir/lib/$testdir/asm" ]; then
asm="$srcdir/lib/$testdir/asm"
fi
ln -s $asm lib/asm
mkdir -p lib
ln -sf "$asm" lib/asm
# create the config
cat <<EOF > config.mak
SRCDIR=$srcdir
PREFIX=$prefix
HOST=$host
ARCH=$arch
ARCH_NAME=$arch_name
PROCESSOR=$processor
CC=$cross_prefix$cc
CXX=$cross_prefix$cxx
LD=$cross_prefix$ld
OBJCOPY=$cross_prefix$objcopy
OBJDUMP=$cross_prefix$objdump
@ -163,5 +193,7 @@ TEST_DIR=$testdir
FIRMWARE=$firmware
ENDIAN=$endian
PRETTY_PRINT_STACKS=$pretty_print_stacks
ENVIRON_DEFAULT=$environ_default
ERRATATXT=errata.txt
U32_LONG_FMT=$u32_long
EOF

View file

@ -25,7 +25,7 @@ void phys_alloc_show(void)
int i;
spin_lock(&lock);
printf("phys_alloc minimum alignment: 0x%" PRIx64 "\n",
printf("phys_alloc minimum alignment: %#" PRIx64 "\n",
(u64)align_min);
for (i = 0; i < nr_regions; ++i)
printf("%016" PRIx64 "-%016" PRIx64 " [%s]\n",
@ -75,10 +75,10 @@ static phys_addr_t phys_alloc_aligned_safe(phys_addr_t size,
size += addr - base;
if ((top_safe - base) < size) {
printf("phys_alloc: requested=0x%" PRIx64
" (align=0x%" PRIx64 "), "
"need=0x%" PRIx64 ", but free=0x%" PRIx64 ". "
"top=0x%" PRIx64 ", top_safe=0x%" PRIx64 "\n",
printf("phys_alloc: requested=%#" PRIx64
" (align=%#" PRIx64 "), "
"need=%#" PRIx64 ", but free=%#" PRIx64 ". "
"top=%#" PRIx64 ", top_safe=%#" PRIx64 "\n",
(u64)size_orig, (u64)align, (u64)size, top_safe - base,
(u64)top, top_safe);
spin_unlock(&lock);

View file

@ -1,8 +1,16 @@
/*
* Set up arguments for main() and prepare environment variables
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#include "libcflat.h"
#include "argv.h"
#include "auxinfo.h"
int __argc;
char *__args;
const char *__args;
char *__argv[100];
char *__environ[200];
@ -15,7 +23,7 @@ static char *copy_ptr = args_copy;
#define isalpha(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') || (c) == '_')
#define isalnum(c) (isalpha(c) || ((c) >= '0' && (c) <= '9'))
static char *skip_blanks(char *p)
static const char *skip_blanks(const char *p)
{
while (isblank(*p))
++p;
@ -24,7 +32,7 @@ static char *skip_blanks(char *p)
void __setup_args(void)
{
char *args = __args;
const char *args = __args;
char **argv = __argv + __argc;
while (*(args = skip_blanks(args)) != '\0') {
@ -36,7 +44,7 @@ void __setup_args(void)
__argc = argv - __argv;
}
void setup_args(char *args)
static void setup_args(const char *args)
{
if (!args)
return;
@ -45,16 +53,13 @@ void setup_args(char *args)
__setup_args();
}
void setup_args_progname(char *args)
void setup_args_progname(const char *args)
{
__argv[0] = copy_ptr;
strcpy(__argv[0], auxinfo.progname);
copy_ptr += strlen(auxinfo.progname) + 1;
++__argc;
if (args) {
__args = args;
__setup_args();
}
setup_args(args);
}
static char *env_eol(char *env)

View file

@ -5,9 +5,9 @@
* adapted from the Linux kernel's include/asm-generic/io.h
* and arch/arm/include/asm/io.h
*
* Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
* Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
* This work is licensed under the terms of the GNU GPL, version 2.
*/
#include "libcflat.h"
#include "asm/page.h"

View file

@ -4,9 +4,9 @@
* asm-generic/page.h
* adapted from the Linux kernel's include/asm-generic/page.h
*
* Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
* Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
* This work is licensed under the terms of the GNU GPL, version 2.
*/
#include <linux/const.h>

View file

@ -1,4 +1,18 @@
#ifndef _ASM_GENERIC_SPINLOCK_H_
#define _ASM_GENERIC_SPINLOCK_H_
#error need architecture specific asm/spinlock.h
struct spinlock {
unsigned int v;
};
static inline void spin_lock(struct spinlock *lock)
{
while (__sync_lock_test_and_set(&lock->v, 1));
}
static inline void spin_unlock(struct spinlock *lock)
{
__sync_lock_release(&lock->v);
}
#endif

View file

@ -1,3 +1,7 @@
/*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#ifndef _AUXINFO_H_
#define _AUXINFO_H_
struct auxinfo {

View file

@ -2,12 +2,12 @@
#define _BITOPS_H_
/*
* Adapated from
* Adapted from
* include/linux/bitops.h
*
* Copyright (C) 2015, Red Hat Inc, Andrew Jones <drjones@redhat.com>
* Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
* This work is licensed under the terms of the GNU GPL, version 2.
*/
#define BITS_PER_LONG_LONG 64

View file

@ -1,3 +1,9 @@
/*
* errata functions
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#ifndef _ERRATA_H_
#define _ERRATA_H_
@ -7,16 +13,33 @@
#define _ERRATA_RELAXED(erratum) errata_relaxed("ERRATA_" # erratum)
#define ERRATA_RELAXED(erratum) _ERRATA_RELAXED(erratum)
static inline bool errata_force(void)
{
char *s = getenv("ERRATA_FORCE");
return s && (*s == '1' || *s == 'y' || *s == 'Y');
}
static inline bool errata(const char *erratum)
{
char *s = getenv(erratum);
char *s;
if (errata_force())
return true;
s = getenv(erratum);
return s && (*s == '1' || *s == 'y' || *s == 'Y');
}
static inline bool errata_relaxed(const char *erratum)
{
char *s = getenv(erratum);
char *s;
if (errata_force())
return true;
s = getenv(erratum);
return !(s && (*s == '0' || *s == 'n' || *s == 'N'));
}

View file

@ -96,15 +96,25 @@ extern int vsnprintf(char *buf, int size, const char *fmt, va_list va)
extern int vprintf(const char *fmt, va_list va)
__attribute__((format(printf, 1, 0)));
void report_prefix_pushf(const char *prefix_fmt, ...)
__attribute__((format(printf, 1, 2)));
extern void report_prefix_push(const char *prefix);
extern void report_prefix_pop(void);
extern void report(const char *msg_fmt, bool pass, ...);
extern void report_xfail(const char *msg_fmt, bool xfail, bool pass, ...);
extern void report_abort(const char *msg_fmt, ...);
extern void report_skip(const char *msg_fmt, ...);
extern void report_info(const char *msg_fmt, ...);
extern void report(const char *msg_fmt, bool pass, ...)
__attribute__((format(printf, 1, 3)));
extern void report_xfail(const char *msg_fmt, bool xfail, bool pass, ...)
__attribute__((format(printf, 1, 4)));
extern void report_abort(const char *msg_fmt, ...)
__attribute__((format(printf, 1, 2)));
extern void report_skip(const char *msg_fmt, ...)
__attribute__((format(printf, 1, 2)));
extern void report_info(const char *msg_fmt, ...)
__attribute__((format(printf, 1, 2)));
extern void report_pass(void);
extern int report_summary(void);
bool simple_glob(const char *text, const char *pattern);
extern void dump_stack(void);
extern void dump_frame_stack(const void *instruction, const void *frame);
@ -124,9 +134,26 @@ do { \
} \
} while (0)
#define assert_msg(cond, fmt, args...) \
do { \
if (!(cond)) { \
printf("%s:%d: assert failed: %s: " fmt "\n", \
__FILE__, __LINE__, #cond, ## args); \
dump_stack(); \
abort(); \
} \
} while (0)
static inline bool is_power_of_2(unsigned long n)
{
return n && !(n & (n - 1));
}
/*
* One byte per bit, a ' between each group of 4 bits, and a null terminator.
*/
#define BINSTR_SZ (sizeof(unsigned long) * 8 + sizeof(unsigned long) * 2)
void binstr(unsigned long x, char out[BINSTR_SZ]);
void print_binstr(unsigned long x);
#endif

View file

@ -48,7 +48,7 @@ void edu_dma(struct pci_edu_dev *dev, iova_t iova,
assert(size <= EDU_DMA_SIZE_MAX);
assert(dev_offset < EDU_DMA_SIZE_MAX);
printf("edu device DMA start %s addr 0x%" PRIx64 " size 0x%lu off 0x%x\n",
printf("edu device DMA start %s addr %#" PRIx64 " size %lu off %#x\n",
from_device ? "FROM" : "TO",
iova, (ulong)size, dev_offset);

View file

@ -131,8 +131,8 @@ static bool pci_testdev_one(struct pci_test_dev_hdr *test,
return (int)count == nr_writes;
}
void pci_testdev_print(struct pci_test_dev_hdr *test,
struct pci_testdev_ops *ops)
static void pci_testdev_print(struct pci_test_dev_hdr *test,
struct pci_testdev_ops *ops)
{
bool io = (ops == &pci_testdev_io_ops);
int i;

View file

@ -49,7 +49,7 @@ bool pci_setup_msi(struct pci_dev *dev, uint64_t msi_addr, uint32_t msi_data)
assert(dev);
if (!dev->msi_offset) {
printf("MSI: dev 0x%x does not support MSI.\n", dev->bdf);
printf("MSI: dev %#x does not support MSI.\n", dev->bdf);
return false;
}
@ -112,6 +112,8 @@ uint32_t pci_bar_mask(uint32_t bar)
uint32_t pci_bar_get(struct pci_dev *dev, int bar_num)
{
ASSERT_BAR_NUM(bar_num);
return pci_config_readl(dev->bdf, PCI_BASE_ADDRESS_0 +
bar_num * 4);
}
@ -134,6 +136,8 @@ static phys_addr_t __pci_bar_get_addr(struct pci_dev *dev, int bar_num)
phys_addr_t pci_bar_get_addr(struct pci_dev *dev, int bar_num)
{
ASSERT_BAR_NUM(bar_num);
return dev->resource[bar_num];
}
@ -141,11 +145,19 @@ void pci_bar_set_addr(struct pci_dev *dev, int bar_num, phys_addr_t addr)
{
int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
assert(addr != INVALID_PHYS_ADDR);
assert(dev->resource[bar_num] != INVALID_PHYS_ADDR);
ASSERT_BAR_NUM(bar_num);
if (pci_bar_is64(dev, bar_num))
ASSERT_BAR_NUM(bar_num + 1);
else
assert((addr >> 32) == 0);
pci_config_writel(dev->bdf, off, (uint32_t)addr);
dev->resource[bar_num] = addr;
if (pci_bar_is64(dev, bar_num)) {
assert(bar_num + 1 < PCI_BAR_NUM);
pci_config_writel(dev->bdf, off + 4, (uint32_t)(addr >> 32));
dev->resource[bar_num + 1] = dev->resource[bar_num];
}
@ -283,10 +295,10 @@ static void pci_cap_print(struct pci_dev *dev, int cap_offset, int cap_id)
break;
}
default:
printf("\tcapability 0x%02x ", cap_id);
printf("\tcapability %#04x ", cap_id);
break;
}
printf("at offset 0x%02x\n", cap_offset);
printf("at offset %#04x\n", cap_offset);
}
void pci_dev_print(struct pci_dev *dev)

View file

@ -18,6 +18,9 @@ enum {
#define PCI_BAR_NUM 6
#define PCI_DEVFN_MAX 256
#define ASSERT_BAR_NUM(bar_num) \
do { assert(bar_num >= 0 && bar_num < PCI_BAR_NUM); } while (0)
#define PCI_BDF_GET_DEVFN(x) ((x) & 0xff)
#define PCI_BDF_GET_BUS(x) (((x) >> 8) & 0xff)

View file

@ -1,3 +1,10 @@
/*
* libc printf and friends
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#include "libcflat.h"
#define BUFSZ 2000
@ -11,6 +18,7 @@ typedef struct pstream {
typedef struct strprops {
char pad;
int npad;
bool alternate;
} strprops_t;
static void addchar(pstream_t *p, char c)
@ -22,7 +30,7 @@ static void addchar(pstream_t *p, char c)
++p->added;
}
void print_str(pstream_t *p, const char *s, strprops_t props)
static void print_str(pstream_t *p, const char *s, strprops_t props)
{
const char *s_orig = s;
int npad = props.npad;
@ -50,7 +58,7 @@ void print_str(pstream_t *p, const char *s, strprops_t props)
static char digits[16] = "0123456789abcdef";
void print_int(pstream_t *ps, long long n, int base, strprops_t props)
static void print_int(pstream_t *ps, long long n, int base, strprops_t props)
{
char buf[sizeof(long) * 3 + 2], *p = buf;
int s = 0, i;
@ -84,10 +92,10 @@ void print_int(pstream_t *ps, long long n, int base, strprops_t props)
print_str(ps, buf, props);
}
void print_unsigned(pstream_t *ps, unsigned long long n, int base,
strprops_t props)
static void print_unsigned(pstream_t *ps, unsigned long long n, int base,
strprops_t props)
{
char buf[sizeof(long) * 3 + 1], *p = buf;
char buf[sizeof(long) * 3 + 3], *p = buf;
int i;
while (n) {
@ -97,6 +105,18 @@ void print_unsigned(pstream_t *ps, unsigned long long n, int base,
if (p == buf)
*p++ = '0';
else if (props.alternate && base == 16) {
if (props.pad == '0') {
addchar(ps, '0');
addchar(ps, 'x');
if (props.npad > 0)
props.npad = MAX(props.npad - 2, 0);
} else {
*p++ = 'x';
*p++ = '0';
}
}
for (i = 0; i < (p - buf) / 2; ++i) {
char tmp;
@ -157,6 +177,9 @@ int vsnprintf(char *buf, int size, const char *fmt, va_list va)
case '\0':
--fmt;
break;
case '#':
props.alternate = true;
goto morefmt;
case '0':
props.pad = '0';
++fmt;
@ -169,6 +192,15 @@ int vsnprintf(char *buf, int size, const char *fmt, va_list va)
case 'l':
++nlong;
goto morefmt;
case 't':
case 'z':
/* Here we only care that sizeof(size_t) == sizeof(long).
* On a 32-bit platform it doesn't matter that size_t is
* typedef'ed to int or long; va_arg will work either way.
* Same for ptrdiff_t (%td).
*/
nlong = 1;
goto morefmt;
case 'd':
switch (nlong) {
case 0:
@ -209,7 +241,7 @@ int vsnprintf(char *buf, int size, const char *fmt, va_list va)
}
break;
case 'p':
print_str(&s, "0x", props);
props.alternate = true;
print_unsigned(&s, (unsigned long)va_arg(va, void *), 16, props);
break;
case 's':
@ -221,7 +253,6 @@ int vsnprintf(char *buf, int size, const char *fmt, va_list va)
}
}
*s.buffer = 0;
++s.added;
return s.added;
}
@ -259,3 +290,33 @@ int printf(const char *fmt, ...)
puts(buf);
return r;
}
void binstr(unsigned long x, char out[BINSTR_SZ])
{
int i;
char *c;
int n;
n = sizeof(unsigned long) * 8;
i = 0;
c = &out[0];
for (;;) {
*c++ = (x & (1ul << (n - i - 1))) ? '1' : '0';
i++;
if (i == n) {
*c = '\0';
break;
}
if (i % 4 == 0)
*c++ = '\'';
}
assert(c + 1 - &out[0] == BINSTR_SZ);
}
void print_binstr(unsigned long x)
{
char out[BINSTR_SZ];
binstr(x, out);
printf("%s", out);
}

View file

@ -17,14 +17,49 @@ static unsigned int tests, failures, xfailures, skipped;
static char prefixes[256];
static struct spinlock lock;
void report_prefix_push(const char *prefix)
#define PREFIX_DELIMITER ": "
void report_pass(void)
{
spin_lock(&lock);
strcat(prefixes, prefix);
strcat(prefixes, ": ");
tests++;
spin_unlock(&lock);
}
void report_prefix_pushf(const char *prefix_fmt, ...)
{
va_list va;
unsigned int len;
int start;
spin_lock(&lock);
len = strlen(prefixes);
assert_msg(len < sizeof(prefixes), "%d >= %zu", len, sizeof(prefixes));
start = len;
va_start(va, prefix_fmt);
len += vsnprintf(&prefixes[len], sizeof(prefixes) - len, prefix_fmt,
va);
va_end(va);
assert_msg(len < sizeof(prefixes), "%d >= %zu", len, sizeof(prefixes));
assert_msg(!strstr(&prefixes[start], PREFIX_DELIMITER),
"Prefix \"%s\" contains delimiter \"" PREFIX_DELIMITER "\"",
&prefixes[start]);
len += snprintf(&prefixes[len], sizeof(prefixes) - len,
PREFIX_DELIMITER);
assert_msg(len < sizeof(prefixes), "%d >= %zu", len, sizeof(prefixes));
spin_unlock(&lock);
}
void report_prefix_push(const char *prefix)
{
report_prefix_pushf("%s", prefix);
}
void report_prefix_pop(void)
{
char *p, *q;
@ -34,9 +69,9 @@ void report_prefix_pop(void)
if (!*prefixes)
return;
for (p = prefixes, q = strstr(p, ": ") + 2;
for (p = prefixes, q = strstr(p, PREFIX_DELIMITER) + 2;
*q;
p = q, q = strstr(p, ": ") + 2)
p = q, q = strstr(p, PREFIX_DELIMITER) + 2)
;
*p = '\0';
@ -46,9 +81,9 @@ void report_prefix_pop(void)
static void va_report(const char *msg_fmt,
bool pass, bool xfail, bool skip, va_list va)
{
char *prefix = skip ? "SKIP"
: xfail ? (pass ? "XPASS" : "XFAIL")
: (pass ? "PASS" : "FAIL");
const char *prefix = skip ? "SKIP"
: xfail ? (pass ? "XPASS" : "XFAIL")
: (pass ? "PASS" : "FAIL");
spin_lock(&lock);

View file

@ -1,3 +1,9 @@
/*
* setjmp/longjmp prototypes
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#ifndef LIBCFLAT_SETJMP_H
#define LIBCFLAT_SETJMP_H 1

View file

@ -1,3 +1,10 @@
/*
* stack related functions
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#include <libcflat.h>
#include <stack.h>

View file

@ -1,3 +1,9 @@
/*
* Header for stack related functions
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#ifndef _STACK_H_
#define _STACK_H_

View file

@ -1,3 +1,10 @@
/*
* libc string functions
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#include "libcflat.h"
unsigned long strlen(const char *buf)
@ -173,3 +180,44 @@ char *getenv(const char *name)
}
return NULL;
}
/* Very simple glob matching. Allows '*' at beginning and end of pattern. */
bool simple_glob(const char *text, const char *pattern)
{
bool star_start = false;
bool star_end = false;
size_t n = strlen(pattern);
char copy[n + 1];
if (pattern[0] == '*') {
pattern += 1;
n -= 1;
star_start = true;
}
strcpy(copy, pattern);
if (n > 0 && pattern[n - 1] == '*') {
n -= 1;
copy[n] = '\0';
star_end = true;
}
if (star_start && star_end)
return strstr(text, copy);
if (star_end)
return strstr(text, copy) == text;
if (star_start) {
size_t text_len = strlen(text);
const char *suffix;
if (n > text_len)
return false;
suffix = text + text_len - n;
return !strcmp(suffix, copy);
}
return !strcmp(text, copy);
}

View file

@ -1,3 +1,9 @@
/*
* Header for libc string functions
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#ifndef __STRING_H
#define __STRING_H

View file

@ -4,6 +4,7 @@
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#include <libcflat.h>
#include "util.h"
int parse_keyval(char *s, long *val)
{

View file

@ -1,9 +1,9 @@
/*
* virtqueue support adapted from the Linux kernel.
*
* Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
* Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
* This work is licensed under the terms of the GNU GPL, version 2.
*/
#include "libcflat.h"
#include "devicetree.h"

View file

@ -3,9 +3,9 @@
/*
* A minimal implementation of virtio-mmio. Adapted from the Linux Kernel.
*
* Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
* Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
* This work is licensed under the terms of the GNU GPL, version 2.
*/
#include "libcflat.h"
#include "asm/page.h"

View file

@ -1,9 +1,9 @@
/*
* virtqueue support adapted from the Linux kernel.
*
* Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
* Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
* This work is licensed under the terms of the GNU GPL, version 2.
*/
#include "libcflat.h"
#include "asm/io.h"

View file

@ -4,9 +4,9 @@
* A minimal implementation of virtio.
* Structures adapted from the Linux Kernel.
*
* Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
* Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
* This work is licensed under the terms of the GNU GPL, version 2.
*/
#include "libcflat.h"

View file

@ -31,6 +31,8 @@
#define PT64_NX_MASK (1ull << 63)
#define PT_ADDR_MASK GENMASK_ULL(51, 12)
#define PT_AD_MASK (PT_ACCESSED_MASK | PT_DIRTY_MASK)
#ifdef __x86_64__
#define PAGE_LEVEL 4
#define PGDIR_WIDTH 9

View file

@ -1,11 +1,6 @@
#ifndef __ASM_SPINLOCK_H
#define __ASM_SPINLOCK_H
struct spinlock {
int v;
};
void spin_lock(struct spinlock *lock);
void spin_unlock(struct spinlock *lock);
#include <asm-generic/spinlock.h>
#endif

View file

@ -69,7 +69,7 @@ static void unhandled_exception(struct ex_regs *regs, bool cpu)
cpu ? "cpu " : "", regs->vector,
exception_mnemonic(regs->vector), regs->rip);
if (regs->vector == 14)
printf("PF at 0x%lx addr 0x%lx\n", regs->rip, read_cr2());
printf("PF at %#lx addr %#lx\n", regs->rip, read_cr2());
printf("error_code=%04lx rflags=%08lx cs=%08lx\n"
"rax=%016lx rcx=%016lx rdx=%016lx rbx=%016lx\n"
@ -117,13 +117,16 @@ static void check_exception_table(struct ex_regs *regs)
unhandled_exception(regs, false);
}
static void (*exception_handlers[32])(struct ex_regs *regs);
static handler exception_handlers[32];
void handle_exception(u8 v, void (*func)(struct ex_regs *regs))
handler handle_exception(u8 v, handler fn)
{
handler old;
old = exception_handlers[v];
if (v < 32)
exception_handlers[v] = func;
exception_handlers[v] = fn;
return old;
}
#ifndef __x86_64__
@ -262,19 +265,6 @@ bool exception_rflags_rf(void)
static char intr_alt_stack[4096];
#ifndef __x86_64__
/*
* GDT, with 6 entries:
* 0x00 - NULL descriptor
* 0x08 - Code segment (ring 0)
* 0x10 - Data segment (ring 0)
* 0x18 - Not present code segment (ring 0)
* 0x20 - Code segment (ring 3)
* 0x28 - Data segment (ring 3)
* 0x30 - Interrupt task
* 0x38 to 0x78 - Free to use for test cases
* 0x80 - Primary task (CPU 0)
*/
void set_gdt_entry(int sel, u32 base, u32 limit, u8 access, u8 gran)
{
int num = sel >> 3;
@ -385,19 +375,21 @@ static void exception_handler(struct ex_regs *regs)
/* longjmp must happen after iret, so do not do it now. */
exception = true;
regs->rip = (unsigned long)&exception_handler_longjmp;
regs->cs = read_cs();
}
bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
void *data)
{
handler old;
jmp_buf jmpbuf;
int ret;
handle_exception(ex, exception_handler);
old = handle_exception(ex, exception_handler);
ret = set_exception_jmpbuf(jmpbuf);
if (ret == 0)
trigger_func(data);
handle_exception(ex, NULL);
handle_exception(ex, old);
return ret;
}

View file

@ -20,6 +20,8 @@ struct ex_regs {
unsigned long rflags;
};
typedef void (*handler)(struct ex_regs *regs);
typedef struct {
u16 prev;
u16 res1;
@ -85,24 +87,59 @@ typedef struct __attribute__((packed)) {
#define UD_VECTOR 6
#define GP_VECTOR 13
#define KERNEL_CS 0x08
#define KERNEL_DS 0x10
#define NP_SEL 0x18
#define USER_CS 0x23
#define USER_DS 0x2b
/*
* selector 32-bit 64-bit
* 0x00 NULL descriptor NULL descriptor
* 0x08 ring-0 code segment (32-bit) ring-0 code segment (64-bit)
* 0x10 ring-0 data segment (32-bit) ring-0 data segment (32/64-bit)
* 0x18 ring-0 code segment (P=0) ring-0 code segment (64-bit, P=0)
* 0x20 intr_alt_stack TSS ring-0 code segment (32-bit)
* 0x28 ring-0 code segment (16-bit) same
* 0x30 ring-0 data segment (16-bit) same
* 0x38 (0x3b) ring-3 code segment (32-bit) same
* 0x40 (0x43) ring-3 data segment (32-bit) ring-3 data segment (32/64-bit)
* 0x48 (0x4b) **unused** ring-3 code segment (64-bit)
* 0x50--0x78 free to use for test cases same
* 0x80 primary TSS (CPU 0) same
*
* Note that the same segment can be used for 32-bit and 64-bit data segments
* (the L bit is only defined for code segments)
*
* Selectors 0x08-0x10 and 0x3b-0x4b are set up for use with the SYSCALL
* and SYSRET instructions.
*/
#define KERNEL_CS 0x08
#define KERNEL_DS 0x10
#define NP_SEL 0x18
#ifdef __x86_64__
#define KERNEL_CS32 0x20
#else
#define TSS_INTR 0x20
#endif
#define KERNEL_CS16 0x28
#define KERNEL_DS16 0x30
#define USER_CS32 0x3b
#define USER_DS 0x43
#ifdef __x86_64__
#define USER_CS64 0x4b
#endif
/* Synonyms */
#define KERNEL_DS32 KERNEL_DS
#define USER_DS32 USER_DS
#ifdef __x86_64__
#define KERNEL_CS64 KERNEL_CS
#define USER_CS USER_CS64
#define KERNEL_DS64 KERNEL_DS
#define KERNEL_CS32 0x30
#define KERNEL_DS32 0x38
#define KERNEL_CS16 0x40
#define KERNEL_DS16 0x48
#define USER_DS64 USER_DS
#else
#define KERNEL_CS32 KERNEL_CS
#define KERNEL_DS32 KERNEL_DS
#define USER_CS USER_CS32
#endif
#define TSS_INTR 0x50
#define FIRST_SPARE_SEL 0x58
#define FIRST_SPARE_SEL 0x50
#define TSS_MAIN 0x80
typedef struct {
@ -153,7 +190,7 @@ void set_idt_dpl(int vec, u16 dpl);
void set_gdt_entry(int sel, u32 base, u32 limit, u8 access, u8 gran);
void set_intr_alt_stack(int e, void *fn);
void print_current_tss_info(void);
void handle_exception(u8 v, void (*func)(struct ex_regs *regs));
handler handle_exception(u8 v, handler fn);
bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
void *data);

View file

@ -3,7 +3,7 @@
static struct spinlock lock;
uint64_t fwcfg_get_u(uint16_t index, int bytes)
static uint64_t fwcfg_get_u(uint16_t index, int bytes)
{
uint64_t r = 0;
uint8_t b;

View file

@ -115,9 +115,9 @@ static void vtd_dump_init_info(void)
/* Major version >= 1 */
assert(((version >> 3) & 0xf) >= 1);
printf("VT-d version: 0x%x\n", version);
printf(" cap: 0x%016lx\n", vtd_readq(DMAR_CAP_REG));
printf(" ecap: 0x%016lx\n", vtd_readq(DMAR_ECAP_REG));
printf("VT-d version: %#x\n", version);
printf(" cap: %#018lx\n", vtd_readq(DMAR_CAP_REG));
printf(" ecap: %#018lx\n", vtd_readq(DMAR_ECAP_REG));
}
static void vtd_setup_root_table(void)
@ -127,7 +127,7 @@ static void vtd_setup_root_table(void)
memset(root, 0, PAGE_SIZE);
vtd_writeq(DMAR_RTADDR_REG, virt_to_phys(root));
vtd_gcmd_or(VTD_GCMD_ROOT);
printf("DMAR table address: 0x%016lx\n", vtd_root_table());
printf("DMAR table address: %#018lx\n", vtd_root_table());
}
static void vtd_setup_ir_table(void)
@ -138,7 +138,7 @@ static void vtd_setup_ir_table(void)
/* 0xf stands for table size (2^(0xf+1) == 65536) */
vtd_writeq(DMAR_IRTA_REG, virt_to_phys(root) | 0xf);
vtd_gcmd_or(VTD_GCMD_IR_TABLE);
printf("IR table address: 0x%016lx\n", vtd_ir_table());
printf("IR table address: %#018lx\n", vtd_ir_table());
}
static void vtd_install_pte(vtd_pte_t *root, iova_t iova,
@ -219,14 +219,14 @@ void vtd_map_range(uint16_t sid, iova_t iova, phys_addr_t pa, size_t size)
ce->present = 1;
/* No error reporting yet */
ce->disable_fault_report = 1;
printf("allocated vt-d context entry for devfn 0x%x\n",
printf("allocated vt-d context entry for devfn %#x\n",
devfn);
} else
slptptr = phys_to_virt(ce->slptptr << VTD_PAGE_SHIFT);
while (size) {
/* TODO: currently we only map 4K pages (level = 1) */
printf("map 4K page IOVA 0x%lx to 0x%lx (sid=0x%04x)\n",
printf("map 4K page IOVA %#lx to %#lx (sid=%#06x)\n",
iova, pa, sid);
vtd_install_pte(slptptr, iova, pa, 1);
size -= VTD_PAGE_SIZE;
@ -324,7 +324,7 @@ bool vtd_setup_msi(struct pci_dev *dev, int vector, int dest_id)
msi_addr.head = 0xfee;
msi_data.subhandle = 0;
printf("%s: msi_addr=0x%" PRIx64 ", msi_data=0x%x\n", __func__,
printf("%s: msi_addr=%#" PRIx64 ", msi_data=%#x\n", __func__,
*(uint64_t *)&msi_addr, *(uint32_t *)&msi_data);
return pci_setup_msi(dev, *(uint64_t *)&msi_addr,

View file

@ -30,13 +30,18 @@
#define X86_CR4_SMAP 0x00200000
#define X86_CR4_PKE 0x00400000
#define X86_EFLAGS_CF 0x00000001
#define X86_EFLAGS_PF 0x00000004
#define X86_EFLAGS_AF 0x00000010
#define X86_EFLAGS_ZF 0x00000040
#define X86_EFLAGS_SF 0x00000080
#define X86_EFLAGS_OF 0x00000800
#define X86_EFLAGS_AC 0x00040000
#define X86_EFLAGS_CF 0x00000001
#define X86_EFLAGS_FIXED 0x00000002
#define X86_EFLAGS_PF 0x00000004
#define X86_EFLAGS_AF 0x00000010
#define X86_EFLAGS_ZF 0x00000040
#define X86_EFLAGS_SF 0x00000080
#define X86_EFLAGS_TF 0x00000100
#define X86_EFLAGS_IF 0x00000200
#define X86_EFLAGS_DF 0x00000400
#define X86_EFLAGS_OF 0x00000800
#define X86_EFLAGS_NT 0x00004000
#define X86_EFLAGS_AC 0x00040000
#define X86_IA32_EFER 0xc0000080
#define X86_EFER_LMA (1UL << 8)
@ -430,4 +435,9 @@ static inline void write_pkru(u32 pkru)
: : "a" (eax), "c" (ecx), "d" (edx));
}
static inline bool is_canonical(u64 addr)
{
return (s64)(addr << 16) >> 16 == addr;
}
#endif

View file

@ -1,5 +1,7 @@
#include <libcflat.h>
#include "processor.h"
#include "atomic.h"
#include "smp.h"
#include "apic.h"
#include "fwcfg.h"
@ -15,6 +17,7 @@ static void *volatile ipi_data;
static volatile int ipi_done;
static volatile bool ipi_wait;
static int _cpu_count;
static atomic_t active_cpus;
static __attribute__((used)) void ipi()
{
@ -27,6 +30,7 @@ static __attribute__((used)) void ipi()
apic_write(APIC_EOI, 0);
}
function(data);
atomic_dec(&active_cpus);
if (wait) {
ipi_done = 1;
apic_write(APIC_EOI, 0);
@ -43,22 +47,6 @@ asm (
#endif
);
void spin_lock(struct spinlock *lock)
{
int v = 1;
do {
asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v));
} while (v);
asm volatile ("" : : : "memory");
}
void spin_unlock(struct spinlock *lock)
{
asm volatile ("" : : : "memory");
lock->v = 0;
}
int cpu_count(void)
{
return _cpu_count;
@ -84,6 +72,7 @@ static void __on_cpu(int cpu, void (*function)(void *data), void *data,
if (cpu == smp_id())
function(data);
else {
atomic_inc(&active_cpus);
ipi_done = 0;
ipi_function = function;
ipi_data = data;
@ -107,6 +96,21 @@ void on_cpu_async(int cpu, void (*function)(void *data), void *data)
__on_cpu(cpu, function, data, 0);
}
void on_cpus(void (*function)(void *data), void *data)
{
int cpu;
for (cpu = cpu_count() - 1; cpu >= 0; --cpu)
on_cpu_async(cpu, function, data);
while (cpus_active() > 1)
pause();
}
int cpus_active(void)
{
return atomic_read(&active_cpus);
}
void smp_init(void)
{
@ -122,4 +126,5 @@ void smp_init(void)
for (i = 1; i < cpu_count(); ++i)
on_cpu(i, setup_smp_id, 0);
atomic_inc(&active_cpus);
}

View file

@ -6,7 +6,9 @@ void smp_init(void);
int cpu_count(void);
int smp_id(void);
int cpus_active(void);
void on_cpu(int cpu, void (*function)(void *data), void *data);
void on_cpu_async(int cpu, void (*function)(void *data), void *data);
void on_cpus(void (*function)(void *data), void *data);
#endif

View file

@ -7,12 +7,29 @@ static void *vfree_top = 0;
static void free_memory(void *mem, unsigned long size)
{
while (size >= PAGE_SIZE) {
*(void **)mem = free;
void *end;
assert_msg((unsigned long) mem % PAGE_SIZE == 0,
"mem not page aligned: %p", mem);
assert_msg(size % PAGE_SIZE == 0, "size not page aligned: %#lx", size);
assert_msg(size == 0 || mem + size > mem,
"mem + size overflow: %p + %#lx", mem, size);
if (size == 0) {
free = NULL;
return;
}
free = mem;
mem += PAGE_SIZE;
size -= PAGE_SIZE;
}
end = mem + size;
while (mem + PAGE_SIZE != end) {
*(void **)mem = (mem + PAGE_SIZE);
mem += PAGE_SIZE;
}
*(void **)mem = NULL;
}
void *alloc_page()
@ -28,6 +45,63 @@ void *alloc_page()
return p;
}
/*
* Allocates (1 << order) physically contiguous and naturally aligned pages.
* Returns NULL if there's no memory left.
*/
void *alloc_pages(unsigned long order)
{
/* Generic list traversal. */
void *prev;
void *curr = NULL;
void *next = free;
/* Looking for a run of length (1 << order). */
unsigned long run = 0;
const unsigned long n = 1ul << order;
const unsigned long align_mask = (n << PAGE_SHIFT) - 1;
void *run_start = NULL;
void *run_prev = NULL;
unsigned long run_next_pa = 0;
unsigned long pa;
assert(order < sizeof(unsigned long) * 8);
for (;;) {
prev = curr;
curr = next;
next = curr ? *((void **) curr) : NULL;
if (!curr)
return 0;
pa = virt_to_phys(curr);
if (run == 0) {
if (!(pa & align_mask)) {
run_start = curr;
run_prev = prev;
run_next_pa = pa + PAGE_SIZE;
run = 1;
}
} else if (pa == run_next_pa) {
run_next_pa += PAGE_SIZE;
run += 1;
} else {
run = 0;
}
if (run == n) {
if (run_prev)
*((void **) run_prev) = next;
else
free = next;
return run_start;
}
}
}
void free_page(void *page)
{
*(void **)page = free;
@ -65,23 +139,62 @@ unsigned long *install_pte(unsigned long *cr3,
return &pt[offset];
}
/*
* Finds last PTE in the mapping of @virt that's at or above @lowest_level. The
* returned PTE isn't necessarily present, but its parent is.
*/
struct pte_search find_pte_level(unsigned long *cr3, void *virt,
int lowest_level)
{
unsigned long *pt = cr3, pte;
unsigned offset;
unsigned long shift;
struct pte_search r;
assert(lowest_level >= 1 && lowest_level <= PAGE_LEVEL);
for (r.level = PAGE_LEVEL;; --r.level) {
shift = (r.level - 1) * PGDIR_WIDTH + 12;
offset = ((unsigned long)virt >> shift) & PGDIR_MASK;
r.pte = &pt[offset];
pte = *r.pte;
if (!(pte & PT_PRESENT_MASK))
return r;
if ((r.level == 2 || r.level == 3) && (pte & PT_PAGE_SIZE_MASK))
return r;
if (r.level == lowest_level)
return r;
pt = phys_to_virt(pte & 0xffffffffff000ull);
}
}
/*
* Returns the leaf PTE in the mapping of @virt (i.e., 4K PTE or a present huge
* PTE). Returns NULL if no leaf PTE exists.
*/
unsigned long *get_pte(unsigned long *cr3, void *virt)
{
int level;
unsigned long *pt = cr3, pte;
unsigned offset;
struct pte_search search;
for (level = PAGE_LEVEL; level > 1; --level) {
offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & PGDIR_MASK;
pte = pt[offset];
if (!(pte & PT_PRESENT_MASK))
return NULL;
if (level == 2 && (pte & PT_PAGE_SIZE_MASK))
return &pt[offset];
pt = phys_to_virt(pte & PT_ADDR_MASK);
}
offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & PGDIR_MASK;
return &pt[offset];
search = find_pte_level(cr3, virt, 1);
return found_leaf_pte(search) ? search.pte : NULL;
}
/*
* Returns the PTE in the mapping of @virt at the given level @pte_level.
* Returns NULL if the PT at @pte_level isn't present (i.e., the mapping at
* @pte_level - 1 isn't present).
*/
unsigned long *get_pte_level(unsigned long *cr3, void *virt, int pte_level)
{
struct pte_search search;
search = find_pte_level(cr3, virt, pte_level);
return search.level == pte_level ? search.pte : NULL;
}
unsigned long *install_large_page(unsigned long *cr3,
@ -99,6 +212,33 @@ unsigned long *install_page(unsigned long *cr3,
return install_pte(cr3, 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK, 0);
}
void install_pages(unsigned long *cr3, unsigned long phys, unsigned long len,
void *virt)
{
unsigned long max = (u64)len + (u64)phys;
assert(phys % PAGE_SIZE == 0);
assert((unsigned long) virt % PAGE_SIZE == 0);
assert(len % PAGE_SIZE == 0);
while (phys + PAGE_SIZE <= max) {
install_page(cr3, phys, virt);
phys += PAGE_SIZE;
virt = (char *) virt + PAGE_SIZE;
}
}
bool any_present_pages(unsigned long *cr3, void *virt, unsigned long len)
{
unsigned long max = (unsigned long) virt + len;
unsigned long curr;
for (curr = (unsigned long) virt; curr < max; curr += PAGE_SIZE) {
unsigned long *ptep = get_pte(cr3, (void *) curr);
if (ptep && (*ptep & PT_PRESENT_MASK))
return true;
}
return false;
}
static void setup_mmu_range(unsigned long *cr3, unsigned long start,
unsigned long len)
@ -110,10 +250,7 @@ static void setup_mmu_range(unsigned long *cr3, unsigned long start,
install_large_page(cr3, phys, (void *)(ulong)phys);
phys += LARGE_PAGE_SIZE;
}
while (phys + PAGE_SIZE <= max) {
install_page(cr3, phys, (void *)(ulong)phys);
phys += PAGE_SIZE;
}
install_pages(cr3, phys, max - phys, (void *)(ulong)phys);
}
static void setup_mmu(unsigned long len)

View file

@ -14,7 +14,27 @@ void *alloc_vpage(void);
void *alloc_vpages(ulong nr);
uint64_t virt_to_phys_cr3(void *mem);
struct pte_search {
int level;
unsigned long *pte;
};
static inline bool found_huge_pte(struct pte_search search)
{
return (search.level == 2 || search.level == 3) &&
(*search.pte & PT_PRESENT_MASK) &&
(*search.pte & PT_PAGE_SIZE_MASK);
}
static inline bool found_leaf_pte(struct pte_search search)
{
return search.level == 1 || found_huge_pte(search);
}
struct pte_search find_pte_level(unsigned long *cr3, void *virt,
int lowest_level);
unsigned long *get_pte(unsigned long *cr3, void *virt);
unsigned long *get_pte_level(unsigned long *cr3, void *virt, int pte_level);
unsigned long *install_pte(unsigned long *cr3,
int pte_level,
void *virt,
@ -22,10 +42,18 @@ unsigned long *install_pte(unsigned long *cr3,
unsigned long *pt_page);
void *alloc_page();
void *alloc_pages(unsigned long order);
void free_page(void *page);
unsigned long *install_large_page(unsigned long *cr3,unsigned long phys,
void *virt);
unsigned long *install_page(unsigned long *cr3, unsigned long phys, void *virt);
void install_pages(unsigned long *cr3, unsigned long phys, unsigned long len,
void *virt);
bool any_present_pages(unsigned long *cr3, void *virt, unsigned long len);
static inline void *current_page_table(void)
{
return phys_to_virt(read_cr3());
}
#endif

View file

@ -1 +1 @@
include $(TEST_DIR)/Makefile.$(ARCH)
include $(SRCDIR)/$(TEST_DIR)/Makefile.$(ARCH)

View file

@ -1,6 +1,6 @@
#This is a make file with common rules for both x86 & x86-64
all: test_cases
all: directories test_cases
cflatobjs += lib/pci.o
cflatobjs += lib/pci-edu.o
@ -16,11 +16,13 @@ cflatobjs += lib/x86/isr.o
cflatobjs += lib/x86/acpi.o
cflatobjs += lib/x86/stack.o
$(libcflat): LDFLAGS += -nostdlib
$(libcflat): CFLAGS += -ffreestanding -I lib
OBJDIRS += lib/x86
CFLAGS += -m$(bits)
CFLAGS += -O1
$(libcflat): LDFLAGS += -nostdlib
$(libcflat): CFLAGS += -ffreestanding -I $(SRCDIR)/lib -I lib
COMMON_CFLAGS += -m$(bits)
COMMON_CFLAGS += -O1
# stack.o relies on frame pointers.
KEEP_FRAME_POINTER := y
@ -31,8 +33,8 @@ libgcc := $(shell $(CC) -m$(bits) --print-libgcc-file-name)
.PRECIOUS: %.elf %.o
FLATLIBS = lib/libcflat.a $(libgcc)
%.elf: %.o $(FLATLIBS) x86/flat.lds $(cstart.o)
$(CC) $(CFLAGS) -nostdlib -o $@ -Wl,-T,x86/flat.lds \
%.elf: %.o $(FLATLIBS) $(SRCDIR)/x86/flat.lds $(cstart.o)
$(CC) $(CFLAGS) -nostdlib -o $@ -Wl,-T,$(SRCDIR)/x86/flat.lds \
$(filter %.o, $^) $(FLATLIBS)
%.flat: %.elf
@ -47,19 +49,20 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
$(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
$(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
$(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
$(TEST_DIR)/hyperv_connections.flat \
ifdef API
tests-common += api/api-sample
tests-common += api/dirty-log
tests-common += api/dirty-log-perf
tests-api = api/api-sample api/dirty-log api/dirty-log-perf
OBJDIRS += api
endif
test_cases: $(tests-common) $(tests)
test_cases: $(tests-common) $(tests) $(tests-api)
$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding -I lib -I lib/x86
$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding -I $(SRCDIR)/lib -I $(SRCDIR)/lib/x86 -I lib
$(TEST_DIR)/realmode.elf: $(TEST_DIR)/realmode.o
$(CC) -m32 -nostdlib -o $@ -Wl,-T,$(TEST_DIR)/realmode.lds $^
$(CC) -m32 -nostdlib -o $@ -Wl,-T,$(SRCDIR)/$(TEST_DIR)/realmode.lds $^
$(TEST_DIR)/realmode.o: bits = 32
@ -69,20 +72,19 @@ $(TEST_DIR)/hyperv_synic.elf: $(TEST_DIR)/hyperv.o
$(TEST_DIR)/hyperv_stimer.elf: $(TEST_DIR)/hyperv.o
$(TEST_DIR)/hyperv_connections.elf: $(TEST_DIR)/hyperv.o
arch_clean:
$(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \
$(TEST_DIR)/.*.d lib/x86/.*.d
$(TEST_DIR)/.*.d lib/x86/.*.d \
$(tests-api) api/*.o api/*.a api/.*.d
api/%.o: CFLAGS += -m32
api/%.o: CXXFLAGS += -m32 -std=gnu++11
api/%: LDLIBS += -lstdc++ -lboost_thread -lpthread -lrt
api/%: LDLIBS += -lstdc++ -lpthread -lrt
api/%: LDFLAGS += -m32
api/libapi.a: api/kvmxx.o api/identity.o api/exception.o api/memmap.o
$(AR) rcs $@ $^
api/api-sample: api/api-sample.o api/libapi.a
api/dirty-log: api/dirty-log.o api/libapi.a
api/dirty-log-perf: api/dirty-log-perf.o api/libapi.a
$(tests-api) : % : %.o api/libapi.a

View file

@ -30,4 +30,4 @@ tests += $(TEST_DIR)/tscdeadline_latency.flat
#tests += $(TEST_DIR)/intel-iommu.flat
include $(TEST_DIR)/Makefile.common
include $(SRCDIR)/$(TEST_DIR)/Makefile.common

View file

@ -1,7 +1,7 @@
cstart.o = $(TEST_DIR)/cstart64.o
bits = 64
ldarch = elf64-x86-64
CFLAGS += -mno-red-zone
COMMON_CFLAGS += -mno-red-zone
cflatobjs += lib/x86/setjmp64.o
cflatobjs += lib/x86/intel-iommu.o
@ -12,12 +12,13 @@ tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \
$(TEST_DIR)/pcid.flat $(TEST_DIR)/debug.flat \
$(TEST_DIR)/ioapic.flat $(TEST_DIR)/memory.flat \
$(TEST_DIR)/pku.flat $(TEST_DIR)/hyperv_clock.flat
tests += $(TEST_DIR)/syscall.flat
tests += $(TEST_DIR)/svm.flat
tests += $(TEST_DIR)/vmx.flat
tests += $(TEST_DIR)/tscdeadline_latency.flat
tests += $(TEST_DIR)/intel-iommu.flat
include $(TEST_DIR)/Makefile.common
include $(SRCDIR)/$(TEST_DIR)/Makefile.common
$(TEST_DIR)/hyperv_clock.elf: $(TEST_DIR)/hyperv_clock.o

View file

@ -52,13 +52,13 @@ static void pf_isr(struct ex_regs *r)
switch (reason) {
case 0:
report("unexpected #PF at %p", false, read_cr2());
report("unexpected #PF at %#lx", false, read_cr2());
break;
case KVM_PV_REASON_PAGE_NOT_PRESENT:
phys = virt_to_phys_cr3(virt);
install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
write_cr3(read_cr3());
report("Got not present #PF token %x virt addr %p phys addr %p",
report("Got not present #PF token %lx virt addr %p phys addr %#" PRIx64,
true, read_cr2(), virt, phys);
while(phys) {
safe_halt(); /* enables irq */
@ -66,7 +66,7 @@ static void pf_isr(struct ex_regs *r)
}
break;
case KVM_PV_REASON_PAGE_READY:
report("Got present #PF token %x", true, read_cr2());
report("Got present #PF token %lx", true, read_cr2());
if ((uint32_t)read_cr2() == ~0)
break;
install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);

View file

@ -34,14 +34,14 @@ gdt32:
.quad 0x00cf9b000000ffff // flat 32-bit code segment
.quad 0x00cf93000000ffff // flat 32-bit data segment
.quad 0x00cf1b000000ffff // flat 32-bit code segment, not present
.quad 0x00cffb000000ffff // 64-bit code segment (user)
.quad 0x00cff3000000ffff // 64-bit data segment (user)
.quad 0 // TSS for task gates
.quad 0x008f9b000000FFFF // 16-bit code segment
.quad 0x008f93000000FFFF // 16-bit data segment
.quad 0x00cffb000000ffff // 32-bit code segment (user)
.quad 0x00cff3000000ffff // 32-bit data segment (user)
.quad 0 // unused
.quad 0 // 10 spare selectors
.quad 0
.quad 0
.quad 0
.quad 0
.quad 0 // 6 spare selectors
.quad 0
.quad 0
.quad 0

View file

@ -53,14 +53,14 @@ gdt64_desc:
gdt64:
.quad 0
.quad 0x00af9b000000ffff // 64-bit code segment
.quad 0x00cf93000000ffff // 64-bit data segment
.quad 0x00cf93000000ffff // 32/64-bit data segment
.quad 0x00af1b000000ffff // 64-bit code segment, not present
.quad 0x00affb000000ffff // 64-bit code segment (user)
.quad 0x00cff3000000ffff // 64-bit data segment (user)
.quad 0x00cf9b000000ffff // 32-bit code segment
.quad 0x00cf92000000ffff // 32-bit data segment
.quad 0x008F9A000000FFFF // 16-bit code segment
.quad 0x008F92000000FFFF // 16-bit data segment
.quad 0x008f9b000000FFFF // 16-bit code segment
.quad 0x008f93000000FFFF // 16-bit data segment
.quad 0x00cffb000000ffff // 32-bit code segment (user)
.quad 0x00cff3000000ffff // 32/64-bit data segment (user)
.quad 0x00affb000000ffff // 64-bit code segment (user)
.quad 0 // 6 spare selectors
.quad 0

View file

@ -171,7 +171,7 @@ static void nmi_iret_isr(struct ex_regs *r)
static void tirq0(isr_regs_t *r)
{
printf("irq0 running\n");
if (test_count != 0)
if (test_count == 1)
test_count++;
eoi();
}

View file

@ -1,25 +1,70 @@
#include "hyperv.h"
#include "asm/io.h"
#include "smp.h"
static void synic_ctl(u8 ctl, u8 vcpu_id, u8 sint)
enum {
HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
HV_TEST_DEV_SINT_ROUTE_DESTROY,
HV_TEST_DEV_SINT_ROUTE_SET_SINT,
HV_TEST_DEV_MSG_CONN_CREATE,
HV_TEST_DEV_MSG_CONN_DESTROY,
HV_TEST_DEV_EVT_CONN_CREATE,
HV_TEST_DEV_EVT_CONN_DESTROY,
};
static void synic_ctl(u32 ctl, u32 vcpu_id, u32 sint, u32 conn_id)
{
outl((ctl << 16)|((vcpu_id) << 8)|sint, 0x3000);
outl((conn_id << 24) | (ctl << 16) | (vcpu_id << 8) | sint, 0x3000);
}
void synic_sint_create(int vcpu, int sint, int vec, bool auto_eoi)
static void sint_enable(u8 sint, u8 vec, bool auto_eoi)
{
wrmsr(HV_X64_MSR_SINT0 + sint,
(u64)vec | ((auto_eoi) ? HV_SYNIC_SINT_AUTO_EOI : 0));
synic_ctl(HV_TEST_DEV_SINT_ROUTE_CREATE, vcpu, sint);
(u64)vec | (auto_eoi ? HV_SYNIC_SINT_AUTO_EOI : 0));
}
void synic_sint_set(int vcpu, int sint)
static void sint_disable(u8 sint)
{
synic_ctl(HV_TEST_DEV_SINT_ROUTE_SET_SINT, vcpu, sint);
wrmsr(HV_X64_MSR_SINT0 + sint, 0xff | HV_SYNIC_SINT_MASKED);
}
void synic_sint_destroy(int vcpu, int sint)
void synic_sint_create(u8 sint, u8 vec, bool auto_eoi)
{
wrmsr(HV_X64_MSR_SINT0 + sint, 0xFF|HV_SYNIC_SINT_MASKED);
synic_ctl(HV_TEST_DEV_SINT_ROUTE_DESTROY, vcpu, sint);
synic_ctl(HV_TEST_DEV_SINT_ROUTE_CREATE, smp_id(), sint, 0);
sint_enable(sint, vec, auto_eoi);
}
void synic_sint_set(u8 vcpu, u8 sint)
{
synic_ctl(HV_TEST_DEV_SINT_ROUTE_SET_SINT, vcpu, sint, 0);
}
void synic_sint_destroy(u8 sint)
{
sint_disable(sint);
synic_ctl(HV_TEST_DEV_SINT_ROUTE_DESTROY, smp_id(), sint, 0);
}
void msg_conn_create(u8 sint, u8 vec, u8 conn_id)
{
synic_ctl(HV_TEST_DEV_MSG_CONN_CREATE, smp_id(), sint, conn_id);
sint_enable(sint, vec, true);
}
void msg_conn_destroy(u8 sint, u8 conn_id)
{
sint_disable(sint);
synic_ctl(HV_TEST_DEV_MSG_CONN_DESTROY, 0, 0, conn_id);
}
void evt_conn_create(u8 sint, u8 vec, u8 conn_id)
{
synic_ctl(HV_TEST_DEV_EVT_CONN_CREATE, smp_id(), sint, conn_id);
sint_enable(sint, vec, true);
}
void evt_conn_destroy(u8 sint, u8 conn_id)
{
sint_disable(sint);
synic_ctl(HV_TEST_DEV_EVT_CONN_DESTROY, 0, 0, conn_id);
}

View file

@ -10,6 +10,9 @@
#define HV_X64_MSR_SYNIC_AVAILABLE (1 << 2)
#define HV_X64_MSR_SYNTIMER_AVAILABLE (1 << 3)
#define HV_X64_MSR_GUEST_OS_ID 0x40000000
#define HV_X64_MSR_HYPERCALL 0x40000001
#define HV_X64_MSR_TIME_REF_COUNT 0x40000020
#define HV_X64_MSR_REFERENCE_TSC 0x40000021
@ -155,10 +158,29 @@ struct hv_message_page {
struct hv_message sint_message[HV_SYNIC_SINT_COUNT];
};
enum {
HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
HV_TEST_DEV_SINT_ROUTE_DESTROY,
HV_TEST_DEV_SINT_ROUTE_SET_SINT
#define HV_EVENT_FLAGS_COUNT (256 * 8)
struct hv_event_flags {
ulong flags[HV_EVENT_FLAGS_COUNT / (8 * sizeof(ulong))];
};
struct hv_event_flags_page {
struct hv_event_flags slot[HV_SYNIC_SINT_COUNT];
};
#define HV_X64_MSR_HYPERCALL_ENABLE 0x1
#define HV_HYPERCALL_FAST (1u << 16)
#define HVCALL_POST_MESSAGE 0x5c
#define HVCALL_SIGNAL_EVENT 0x5d
struct hv_input_post_message {
u32 connectionid;
u32 reserved;
u32 message_type;
u32 payload_size;
u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
};
static inline bool synic_supported(void)
@ -176,9 +198,13 @@ static inline bool hv_time_ref_counter_supported(void)
return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE;
}
void synic_sint_create(int vcpu, int sint, int vec, bool auto_eoi);
void synic_sint_set(int vcpu, int sint);
void synic_sint_destroy(int vcpu, int sint);
void synic_sint_create(u8 sint, u8 vec, bool auto_eoi);
void synic_sint_set(u8 vcpu, u8 sint);
void synic_sint_destroy(u8 sint);
void msg_conn_create(u8 sint, u8 vec, u8 conn_id);
void msg_conn_destroy(u8 sint, u8 conn_id);
void evt_conn_create(u8 sint, u8 vec, u8 conn_id);
void evt_conn_destroy(u8 sint, u8 conn_id);
struct hv_reference_tsc_page {
uint32_t tsc_sequence;

View file

@ -19,7 +19,7 @@ static inline u64 scale_delta(u64 delta, u64 mul_frac)
u64 product, unused;
__asm__ (
"mul %3"
"mulq %3"
: "=d" (product), "=a" (unused) : "1" (delta), "rm" ((u64)mul_frac) );
return product;
@ -55,7 +55,6 @@ uint64_t hv_clock_read(void)
return hvclock_tsc_to_ticks(&shadow, rdtsc());
}
atomic_t cpus_left;
bool ok[MAX_CPU];
uint64_t loops[MAX_CPU];
@ -99,7 +98,6 @@ static void hv_clock_test(void *data)
if (!got_drift)
printf("delta on CPU %d was %d...%d\n", smp_id(), min_delta, max_delta);
barrier();
atomic_dec(&cpus_left);
}
static void check_test(int ncpus)
@ -107,13 +105,7 @@ static void check_test(int ncpus)
int i;
bool pass;
atomic_set(&cpus_left, ncpus);
for (i = ncpus - 1; i >= 0; i--)
on_cpu_async(i, hv_clock_test, NULL);
/* Wait for the end of other vcpu */
while(atomic_read(&cpus_left))
;
on_cpus(hv_clock_test, NULL);
pass = true;
for (i = ncpus - 1; i >= 0; i--)
@ -134,7 +126,6 @@ static void hv_perf_test(void *data)
} while(t < end);
loops[smp_id()] = local_loops;
atomic_dec(&cpus_left);
}
static void perf_test(int ncpus)
@ -142,13 +133,7 @@ static void perf_test(int ncpus)
int i;
uint64_t total_loops;
atomic_set(&cpus_left, ncpus);
for (i = ncpus - 1; i >= 0; i--)
on_cpu_async(i, hv_perf_test, NULL);
/* Wait for the end of other vcpu */
while(atomic_read(&cpus_left))
;
on_cpus(hv_perf_test, NULL);
total_loops = 0;
for (i = ncpus - 1; i >= 0; i--)
@ -167,6 +152,10 @@ int main(int ac, char **av)
setup_vm();
smp_init();
ncpus = cpu_count();
if (ncpus > MAX_CPU)
report_abort("number cpus exceeds %d", MAX_CPU);
hv_clock = alloc_page();
wrmsr(HV_X64_MSR_REFERENCE_TSC, (u64)(uintptr_t)hv_clock | 1);
report("MSR value after enabling",
@ -195,10 +184,6 @@ int main(int ac, char **av)
"TSC reference %" PRId64" (delta %" PRId64")\n",
ref2, ref2 - ref1, tsc2, t2, t2 - t1);
ncpus = cpu_count();
if (ncpus > MAX_CPU)
ncpus = MAX_CPU;
check_test(ncpus);
perf_test(ncpus);

View file

@ -19,8 +19,6 @@
#define SINT2_NUM 3
#define ONE_MS_IN_100NS 10000
static atomic_t g_cpus_comp_count;
static int g_cpus_count;
static struct spinlock g_synic_alloc_lock;
struct stimer {
@ -216,20 +214,13 @@ static void synic_disable(void)
synic_free_page(svcpu->msg_page);
}
static void cpu_comp(void)
{
atomic_inc(&g_cpus_comp_count);
}
static void stimer_test_prepare(void *ctx)
{
int vcpu = smp_id();
write_cr3((ulong)ctx);
synic_enable();
synic_sint_create(vcpu, SINT1_NUM, SINT1_VEC, false);
synic_sint_create(vcpu, SINT2_NUM, SINT2_VEC, true);
cpu_comp();
synic_sint_create(SINT1_NUM, SINT1_VEC, false);
synic_sint_create(SINT2_NUM, SINT2_VEC, true);
}
static void stimer_test_periodic(int vcpu, struct stimer *timer1,
@ -280,6 +271,35 @@ static void stimer_test_auto_enable_periodic(int vcpu, struct stimer *timer)
stimer_shutdown(timer);
}
static void stimer_test_one_shot_busy(int vcpu, struct stimer *timer)
{
struct hv_message_page *msg_page = g_synic_vcpu[vcpu].msg_page;
struct hv_message *msg = &msg_page->sint_message[timer->sint];
msg->header.message_type = HVMSG_TIMER_EXPIRED;
wmb();
stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM);
do
rmb();
while (!msg->header.message_flags.msg_pending);
report("no timer fired while msg slot busy: vcpu %d",
!atomic_read(&timer->fire_count), vcpu);
msg->header.message_type = HVMSG_NONE;
wmb();
wrmsr(HV_X64_MSR_EOM, 0);
while (atomic_read(&timer->fire_count) < 1) {
pause();
}
report("timer resumed when msg slot released: vcpu %d", true, vcpu);
stimer_shutdown(timer);
}
static void stimer_test(void *ctx)
{
int vcpu = smp_id();
@ -295,33 +315,17 @@ static void stimer_test(void *ctx)
stimer_test_one_shot(vcpu, timer1);
stimer_test_auto_enable_one_shot(vcpu, timer2);
stimer_test_auto_enable_periodic(vcpu, timer1);
stimer_test_one_shot_busy(vcpu, timer1);
irq_disable();
cpu_comp();
}
static void stimer_test_cleanup(void *ctx)
{
int vcpu = smp_id();
stimers_shutdown();
synic_sint_destroy(vcpu, SINT1_NUM);
synic_sint_destroy(vcpu, SINT2_NUM);
synic_sint_destroy(SINT1_NUM);
synic_sint_destroy(SINT2_NUM);
synic_disable();
cpu_comp();
}
static void on_each_cpu_async_wait(void (*func)(void *ctx), void *ctx)
{
int i;
atomic_set(&g_cpus_comp_count, 0);
for (i = 0; i < g_cpus_count; i++) {
on_cpu_async(i, func, ctx);
}
while (atomic_read(&g_cpus_comp_count) != g_cpus_count) {
pause();
}
}
static void stimer_test_all(void)
@ -332,20 +336,17 @@ static void stimer_test_all(void)
smp_init();
enable_apic();
ncpus = cpu_count();
if (ncpus > MAX_CPUS)
report_abort("number cpus exceeds %d", MAX_CPUS);
printf("cpus = %d\n", ncpus);
handle_irq(SINT1_VEC, stimer_isr);
handle_irq(SINT2_VEC, stimer_isr_auto_eoi);
ncpus = cpu_count();
if (ncpus > MAX_CPUS) {
ncpus = MAX_CPUS;
}
printf("cpus = %d\n", ncpus);
g_cpus_count = ncpus;
on_each_cpu_async_wait(stimer_test_prepare, (void *)read_cr3());
on_each_cpu_async_wait(stimer_test, NULL);
on_each_cpu_async_wait(stimer_test_cleanup, NULL);
on_cpus(stimer_test_prepare, (void *)read_cr3());
on_cpus(stimer_test, NULL);
on_cpus(stimer_test_cleanup, NULL);
}
int main(int ac, char **av)

View file

@ -12,7 +12,6 @@
#define MAX_CPUS 4
static atomic_t isr_enter_count[MAX_CPUS];
static atomic_t cpus_comp_count;
static void synic_sint_auto_eoi_isr(isr_regs_t *regs)
{
@ -69,7 +68,7 @@ static void synic_sints_prepare(int vcpu)
for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
vec = sint_vecs[i].vec;
auto_eoi = sint_vecs[i].auto_eoi;
synic_sint_create(vcpu, i, vec, auto_eoi);
synic_sint_create(i, vec, auto_eoi);
}
}
@ -90,8 +89,8 @@ static void synic_test_prepare(void *ctx)
}
r = rdmsr(HV_X64_MSR_EOM);
if (r != 0) {
report("Hyper-V SynIC test, EOM read 0x%llx", false, r);
goto ret;
report("Hyper-V SynIC test, EOM read %#" PRIx64, false, r);
return;
}
wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(alloc_page()) |
@ -101,8 +100,6 @@ static void synic_test_prepare(void *ctx)
wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE);
synic_sints_prepare(smp_id());
ret:
atomic_inc(&cpus_comp_count);
}
static void synic_sints_test(int dst_vcpu)
@ -125,24 +122,20 @@ static void synic_test(void *ctx)
irq_enable();
synic_sints_test(dst_vcpu);
atomic_inc(&cpus_comp_count);
}
static void synic_test_cleanup(void *ctx)
{
int vcpu = smp_id();
int i;
irq_enable();
for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
synic_sint_destroy(vcpu, i);
wrmsr(HV_X64_MSR_SINT0 + i, 0xFF|HV_SYNIC_SINT_MASKED);
synic_sint_destroy(i);
}
wrmsr(HV_X64_MSR_SCONTROL, 0);
wrmsr(HV_X64_MSR_SIMP, 0);
wrmsr(HV_X64_MSR_SIEFP, 0);
atomic_inc(&cpus_comp_count);
}
int main(int ac, char **av)
@ -156,40 +149,25 @@ int main(int ac, char **av)
smp_init();
enable_apic();
synic_prepare_sint_vecs();
ncpus = cpu_count();
if (ncpus > MAX_CPUS) {
ncpus = MAX_CPUS;
}
if (ncpus > MAX_CPUS)
report_abort("number cpus exceeds %d", MAX_CPUS);
printf("ncpus = %d\n", ncpus);
atomic_set(&cpus_comp_count, 0);
for (i = 0; i < ncpus; i++) {
on_cpu_async(i, synic_test_prepare, (void *)read_cr3());
}
printf("prepare\n");
while (atomic_read(&cpus_comp_count) != ncpus) {
pause();
}
synic_prepare_sint_vecs();
printf("prepare\n");
on_cpus(synic_test_prepare, (void *)read_cr3());
atomic_set(&cpus_comp_count, 0);
for (i = 0; i < ncpus; i++) {
printf("test %d -> %d\n", i, ncpus - 1 - i);
on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i));
}
while (atomic_read(&cpus_comp_count) != ncpus) {
while (cpus_active() > 1)
pause();
}
atomic_set(&cpus_comp_count, 0);
for (i = 0; i < ncpus; i++) {
on_cpu_async(i, synic_test_cleanup, NULL);
}
printf("cleanup\n");
while (atomic_read(&cpus_comp_count) != ncpus) {
pause();
}
on_cpus(synic_test_cleanup, NULL);
ok = true;
for (i = 0; i < ncpus; ++i) {

View file

@ -17,7 +17,6 @@ struct test_info {
u64 stalls; /* stall count */
long long worst; /* worst warp */
volatile cycle_t last; /* last cycle seen by test */
atomic_t ncpus; /* number of cpu in the test*/
int check; /* check cycle ? */
};
@ -78,29 +77,20 @@ static void kvm_clock_test(void *data)
if (!((unsigned long)i & 31))
asm volatile("rep; nop");
}
atomic_dec(&hv_test_info->ncpus);
}
static int cycle_test(int ncpus, int check, struct test_info *ti)
static int cycle_test(int check, struct test_info *ti)
{
int i;
unsigned long long begin, end;
begin = rdtsc();
atomic_set(&ti->ncpus, ncpus);
ti->check = check;
for (i = ncpus - 1; i >= 0; i--)
on_cpu_async(i, kvm_clock_test, (void *)ti);
/* Wait for the end of other vcpu */
while(atomic_read(&ti->ncpus))
;
on_cpus(kvm_clock_test, ti);
end = rdtsc();
printf("Total vcpus: %d\n", ncpus);
printf("Total vcpus: %d\n", cpu_count());
printf("Test loops: %ld\n", loops);
if (check == 1) {
printf("Total warps: %" PRId64 "\n", ti->warps);
@ -129,9 +119,9 @@ int main(int ac, char **av)
ncpus = cpu_count();
if (ncpus > MAX_CPU)
ncpus = MAX_CPU;
for (i = 0; i < ncpus; ++i)
on_cpu(i, kvm_clock_init, (void *)0);
report_abort("number cpus exceeds %d", MAX_CPU);
on_cpus(kvm_clock_init, NULL);
if (ac > 2) {
printf("Wallclock test, threshold %ld\n", threshold);
@ -143,26 +133,25 @@ int main(int ac, char **av)
printf("Check the stability of raw cycle ...\n");
pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT
| PVCLOCK_RAW_CYCLE_BIT);
if (cycle_test(ncpus, 1, &ti[0]))
if (cycle_test(1, &ti[0]))
printf("Raw cycle is not stable\n");
else
printf("Raw cycle is stable\n");
pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT);
printf("Monotonic cycle test:\n");
nerr += cycle_test(ncpus, 1, &ti[1]);
nerr += cycle_test(1, &ti[1]);
printf("Measure the performance of raw cycle ...\n");
pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT
| PVCLOCK_RAW_CYCLE_BIT);
cycle_test(ncpus, 0, &ti[2]);
cycle_test(0, &ti[2]);
printf("Measure the performance of adjusted cycle ...\n");
pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT);
cycle_test(ncpus, 0, &ti[3]);
cycle_test(0, &ti[3]);
for (i = 0; i < ncpus; ++i)
on_cpu(i, kvm_clock_clear, (void *)0);
on_cpus(kvm_clock_clear, NULL);
return nerr > 0 ? 1 : 0;
}

View file

@ -6,7 +6,7 @@
struct msr_info {
int index;
char *name;
const char *name;
struct tc {
int valid;
unsigned long long value;
@ -78,37 +78,20 @@ static void test_msr_rw(int msr_index, unsigned long long input, unsigned long l
{
unsigned long long r = 0;
int index;
char *sptr;
const char *sptr;
if ((index = find_msr_info(msr_index)) != -1) {
sptr = msr_info[index].name;
} else {
printf("couldn't find name for msr # 0x%x, skipping\n", msr_index);
printf("couldn't find name for msr # %#x, skipping\n", msr_index);
return;
}
wrmsr(msr_index, input);
r = rdmsr(msr_index);
if (expected != r) {
printf("testing %s: output = 0x%x:0x%x expected = 0x%x:0x%x\n", sptr,
printf("testing %s: output = %#x:%#x expected = %#x:%#x\n", sptr,
(u32)(r >> 32), (u32)r, (u32)(expected >> 32), (u32)expected);
}
report(sptr, expected == r);
}
static void test_syscall_lazy_load(void)
{
#ifdef __x86_64__
extern void syscall_target();
u16 cs = read_cs(), ss = read_ss();
ulong tmp;
wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE);
wrmsr(MSR_LSTAR, (ulong)syscall_target);
wrmsr(MSR_STAR, (uint64_t)cs << 32);
asm volatile("pushf; syscall; syscall_target: popf" : "=c"(tmp) : : "r11");
write_ss(ss);
// will crash horribly if broken
report("MSR_*STAR eager loading", true);
#endif
report("%s", expected == r, sptr);
}
int main(int ac, char **av)
@ -124,8 +107,6 @@ int main(int ac, char **av)
}
}
test_syscall_lazy_load();
return report_summary();
}

View file

@ -73,7 +73,7 @@ union cpuid10_edx {
} edx;
struct pmu_event {
char *name;
const char *name;
uint32_t unit_sel;
int min;
int max;

View file

@ -1,51 +0,0 @@
#!/bin/bash
[ -z "$STANDALONE" ] && source scripts/arch-run.bash
qemubinarysearch="${QEMU:-qemu-kvm qemu-system-x86_64}"
for qemucmd in ${qemubinarysearch}
do
unset QEMUFOUND
unset qemu
if ! [ -z "${QEMUFOUND=$(${qemucmd} --help 2>/dev/null | grep "QEMU")}" ] &&
${qemucmd} -device '?' 2>&1 | grep -F -e \"testdev\" -e \"pc-testdev\" > /dev/null;
then
qemu="${qemucmd}"
break
fi
done
if [ -z "${QEMUFOUND}" ]
then
echo "A QEMU binary was not found, You can set a custom location by using the QEMU=<path> environment variable "
exit 2
elif [ -z "${qemu}" ]
then
echo "No Qemu test device support found"
exit 2
fi
if
${qemu} -device '?' 2>&1 | grep -F "pci-testdev" > /dev/null;
then
pci_testdev="-device pci-testdev"
else
pci_testdev=""
fi
if
${qemu} -device '?' 2>&1 | grep -F "pc-testdev" > /dev/null;
then
pc_testdev="-device pc-testdev -device isa-debug-exit,iobase=0xf4,iosize=0x4"
else
pc_testdev="-device testdev,chardev=testlog -chardev file,id=testlog,path=msr.out"
fi
command="${qemu} -nodefaults -enable-kvm $pc_testdev -vnc none -serial stdio $pci_testdev $hyperv_testdev"
[ -f "$ENV" ] && command+=" -initrd $ENV"
command+=" -kernel"
command="$(timeout_cmd) $command"
echo ${command} "$@"
run_qemu ${command} "$@"

View file

@ -23,7 +23,7 @@ void test_rdtscp(u64 aux)
wrmsr(MSR_TSC_AUX, aux);
rdtscp(&ecx);
report("Test RDTSCP %d", ecx == aux, aux);
report("Test RDTSCP %" PRIu64, ecx == aux, aux);
}
int main()

View file

@ -1,226 +0,0 @@
##############################################################################
# unittest configuration
#
# [unittest_name]
# file = <name>.flat # Name of the flat file to be used.
# smp = <num> # Number of processors the VM will use
# # during this test. Use $MAX_SMP to use
# # the maximum the host supports. Defaults
# # to one.
# extra_params = -append <params...> # Additional parameters used.
# arch = i386|x86_64 # Select one if the test case is
# # specific to only one.
# groups = <group_name1> <group_name2> ... # Used to identify test cases
# # with run_tests -g ...
# # Specify group_name=nodefault
# # to have test not run by
# # default
# accel = kvm|tcg # Optionally specify if test must run with
# # kvm or tcg. If not specified, then kvm will
# # be used when available.
# timeout = <duration> # Optionally specify a timeout.
# check = <path>=<value> # check a file for a particular value before running
# # a test. The check line can contain multiple files
# # to check separated by a space but each check
# # parameter needs to be of the form <path>=<value>
##############################################################################
[apic-split]
file = apic.flat
smp = 2
extra_params = -cpu qemu64,+x2apic,+tsc-deadline -machine kernel_irqchip=split
arch = x86_64
[ioapic-split]
file = ioapic.flat
extra_params = -cpu qemu64 -machine kernel_irqchip=split
arch = x86_64
[apic]
file = apic.flat
smp = 2
extra_params = -cpu qemu64,+x2apic,+tsc-deadline
arch = x86_64
timeout = 30
[ioapic]
file = ioapic.flat
extra_params = -cpu qemu64
arch = x86_64
[smptest]
file = smptest.flat
smp = 2
[smptest3]
file = smptest.flat
smp = 3
[vmexit_cpuid]
file = vmexit.flat
extra_params = -append 'cpuid'
groups = vmexit
[vmexit_vmcall]
file = vmexit.flat
extra_params = -append 'vmcall'
groups = vmexit
[vmexit_mov_from_cr8]
file = vmexit.flat
extra_params = -append 'mov_from_cr8'
groups = vmexit
[vmexit_mov_to_cr8]
file = vmexit.flat
extra_params = -append 'mov_to_cr8'
groups = vmexit
[vmexit_inl_pmtimer]
file = vmexit.flat
extra_params = -append 'inl_from_pmtimer'
groups = vmexit
[vmexit_ipi]
file = vmexit.flat
smp = 2
extra_params = -append 'ipi'
groups = vmexit
[vmexit_ipi_halt]
file = vmexit.flat
smp = 2
extra_params = -append 'ipi_halt'
groups = vmexit
[vmexit_ple_round_robin]
file = vmexit.flat
extra_params = -append 'ple_round_robin'
groups = vmexit
[access]
file = access.flat
arch = x86_64
[smap]
file = smap.flat
extra_params = -cpu host
[pku]
file = pku.flat
arch = x86_64
extra_params = -cpu host
#[asyncpf]
#file = asyncpf.flat
[emulator]
file = emulator.flat
arch = x86_64
[eventinj]
file = eventinj.flat
[hypercall]
file = hypercall.flat
[idt_test]
file = idt_test.flat
arch = x86_64
#[init]
#file = init.flat
[msr]
file = msr.flat
[pmu]
file = pmu.flat
extra_params = -cpu host
check = /proc/sys/kernel/nmi_watchdog=0
[port80]
file = port80.flat
[realmode]
file = realmode.flat
[s3]
file = s3.flat
[sieve]
file = sieve.flat
[tsc]
file = tsc.flat
extra_params = -cpu kvm64,+rdtscp
[tsc_adjust]
file = tsc_adjust.flat
extra_params = -cpu host
[xsave]
file = xsave.flat
arch = x86_64
extra_params = -cpu host
[rmap_chain]
file = rmap_chain.flat
arch = x86_64
[svm]
file = svm.flat
smp = 2
extra_params = -cpu qemu64,+svm
arch = x86_64
[taskswitch]
file = taskswitch.flat
arch = i386
groups = tasks
[taskswitch2]
file = taskswitch2.flat
arch = i386
groups = tasks
[kvmclock_test]
file = kvmclock_test.flat
smp = 2
extra_params = --append "10000000 `date +%s`"
[pcid]
file = pcid.flat
extra_params = -cpu qemu64,+pcid
arch = x86_64
[vmx]
file = vmx.flat
extra_params = -cpu host,+vmx
arch = x86_64
[debug]
file = debug.flat
arch = x86_64
[hyperv_synic]
file = hyperv_synic.flat
smp = 2
extra_params = -cpu kvm64,hv_synic -device hyperv-testdev
[hyperv_stimer]
file = hyperv_stimer.flat
smp = 2
extra_params = -cpu kvm64,hv_time,hv_synic,hv_stimer -device hyperv-testdev
[hyperv_clock]
file = hyperv_clock.flat
smp = 2
extra_params = -cpu kvm64,hv_time
[intel_iommu]
file = intel-iommu.flat
arch = x86_64
timeout = 30
smp = 4
extra_params = -M q35,kernel-irqchip=split -device intel-iommu,intremap=on,eim=off -device edu

View file

@ -387,6 +387,32 @@ static bool pci_io_next(struct test *test)
return ret;
}
static int has_tscdeadline(void)
{
uint32_t lvtt;
if (cpuid(1).c & (1 << 24)) {
lvtt = APIC_LVT_TIMER_TSCDEADLINE | IPI_TEST_VECTOR;
apic_write(APIC_LVTT, lvtt);
return 1;
} else {
return 0;
}
}
static void tscdeadline_immed(void)
{
wrmsr(MSR_IA32_TSCDEADLINE, rdtsc());
asm volatile("nop");
}
static void tscdeadline(void)
{
x = 0;
wrmsr(MSR_IA32_TSCDEADLINE, rdtsc()+3000);
while (x == 0) barrier();
}
static struct test tests[] = {
{ cpuid_test, "cpuid", .parallel = 1, },
{ vmcall, "vmcall", .parallel = 1, },
@ -399,6 +425,8 @@ static struct test tests[] = {
{ inl_nop_kernel, "inl_from_kernel", .parallel = 1 },
{ outl_elcr_kernel, "outl_to_kernel", .parallel = 1 },
{ mov_dr, "mov_dr", .parallel = 1 },
{ tscdeadline_immed, "tscdeadline_immed", has_tscdeadline, .parallel = 1, },
{ tscdeadline, "tscdeadline", has_tscdeadline, .parallel = 1, },
{ self_ipi_sti_nop, "self_ipi_sti_nop", .parallel = 0, },
{ self_ipi_sti_hlt, "self_ipi_sti_hlt", .parallel = 0, },
{ self_ipi_tpr, "self_ipi_tpr", .parallel = 0, },
@ -419,7 +447,6 @@ static struct test tests[] = {
};
unsigned iterations;
static atomic_t nr_cpus_done;
static void run_test(void *_func)
{
@ -428,8 +455,6 @@ static void run_test(void *_func)
for (i = 0; i < iterations; ++i)
func();
atomic_inc(&nr_cpus_done);
}
static bool do_test(struct test *test)
@ -463,11 +488,7 @@ static bool do_test(struct test *test)
for (i = 0; i < iterations; ++i)
func();
} else {
atomic_set(&nr_cpus_done, 0);
for (i = cpu_count(); i > 0; i--)
on_cpu_async(i-1, run_test, func);
while (atomic_read(&nr_cpus_done) < cpu_count())
;
on_cpus(run_test, func);
}
t2 = rdtsc();
} while ((t2 - t1) < GOAL);
@ -509,8 +530,7 @@ int main(int ac, char **av)
nr_cpus = cpu_count();
irq_enable();
for (i = cpu_count(); i > 0; i--)
on_cpu(i-1, enable_nx, 0);
on_cpus(enable_nx, NULL);
fadt = find_acpi_table_addr(FACP_SIGNATURE);
pm_tmr_blk = fadt->pm_tmr_blk;
@ -524,7 +544,7 @@ int main(int ac, char **av)
membar = pcidev.resource[PCI_TESTDEV_BAR_MEM];
pci_test.memaddr = ioremap(membar, PAGE_SIZE);
pci_test.iobar = pcidev.resource[PCI_TESTDEV_BAR_IO];
printf("pci-testdev at 0x%x membar %lx iobar %x\n",
printf("pci-testdev at %#x membar %lx iobar %x\n",
pcidev.bdf, membar, pci_test.iobar);
}

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,7 @@
#include "processor.h"
#include "bitops.h"
#include "asm/page.h"
#include "asm/io.h"
struct vmcs {
u32 revision_id; /* vmcs revision identifier */
@ -13,6 +14,11 @@ struct vmcs {
char data[0];
};
struct invvpid_operand {
u64 vpid;
u64 gla;
};
struct regs {
u64 rax;
u64 rcx;
@ -54,6 +60,8 @@ struct vmx_test {
int (*entry_failure_handler)(struct vmentry_failure *failure);
struct vmcs *vmcs;
int exits;
/* Alternative test interface. */
void (*v2)(void);
};
union vmx_basic {
@ -108,6 +116,7 @@ enum Encoding {
GUEST_SEL_LDTR = 0x080cul,
GUEST_SEL_TR = 0x080eul,
GUEST_INT_STATUS = 0x0810ul,
GUEST_PML_INDEX = 0x0812ul,
/* 16-Bit Host State Fields */
HOST_SEL_ES = 0x0c00ul,
@ -132,6 +141,9 @@ enum Encoding {
APIC_ACCS_ADDR = 0x2014ul,
EPTP = 0x201aul,
EPTP_HI = 0x201bul,
PMLADDR = 0x200eul,
PMLADDR_HI = 0x200ful,
/* 64-Bit Readonly Data Field */
INFO_PHYS_ADDR = 0x2400ul,
@ -317,7 +329,15 @@ enum Reason {
VMX_PREEMPT = 52,
VMX_INVVPID = 53,
VMX_WBINVD = 54,
VMX_XSETBV = 55
VMX_XSETBV = 55,
VMX_APIC_WRITE = 56,
VMX_RDRAND = 57,
VMX_INVPCID = 58,
VMX_VMFUNC = 59,
VMX_RDSEED = 61,
VMX_PML_FULL = 62,
VMX_XSAVES = 63,
VMX_XRSTORS = 64,
};
enum Ctrl_exi {
@ -375,6 +395,7 @@ enum Ctrl1 {
CPU_URG = 1ul << 7,
CPU_WBINVD = 1ul << 6,
CPU_RDRAND = 1ul << 11,
CPU_PML = 1ul << 17,
};
enum Intr_type {
@ -396,6 +417,37 @@ enum Intr_type {
#define INTR_INFO_INTR_TYPE_SHIFT 8
/*
* VM-instruction error numbers
*/
enum vm_instruction_error_number {
VMXERR_VMCALL_IN_VMX_ROOT_OPERATION = 1,
VMXERR_VMCLEAR_INVALID_ADDRESS = 2,
VMXERR_VMCLEAR_VMXON_POINTER = 3,
VMXERR_VMLAUNCH_NONCLEAR_VMCS = 4,
VMXERR_VMRESUME_NONLAUNCHED_VMCS = 5,
VMXERR_VMRESUME_AFTER_VMXOFF = 6,
VMXERR_ENTRY_INVALID_CONTROL_FIELD = 7,
VMXERR_ENTRY_INVALID_HOST_STATE_FIELD = 8,
VMXERR_VMPTRLD_INVALID_ADDRESS = 9,
VMXERR_VMPTRLD_VMXON_POINTER = 10,
VMXERR_VMPTRLD_INCORRECT_VMCS_REVISION_ID = 11,
VMXERR_UNSUPPORTED_VMCS_COMPONENT = 12,
VMXERR_VMWRITE_READ_ONLY_VMCS_COMPONENT = 13,
VMXERR_VMXON_IN_VMX_ROOT_OPERATION = 15,
VMXERR_ENTRY_INVALID_EXECUTIVE_VMCS_POINTER = 16,
VMXERR_ENTRY_NONLAUNCHED_EXECUTIVE_VMCS = 17,
VMXERR_ENTRY_EXECUTIVE_VMCS_POINTER_NOT_VMXON_POINTER = 18,
VMXERR_VMCALL_NONCLEAR_VMCS = 19,
VMXERR_VMCALL_INVALID_VM_EXIT_CONTROL_FIELDS = 20,
VMXERR_VMCALL_INCORRECT_MSEG_REVISION_ID = 22,
VMXERR_VMXOFF_UNDER_DUAL_MONITOR_TREATMENT_OF_SMIS_AND_SMM = 23,
VMXERR_VMCALL_INVALID_SMM_MONITOR_FEATURES = 24,
VMXERR_ENTRY_INVALID_VM_EXECUTION_CONTROL_FIELDS_IN_EXECUTIVE_VMCS = 25,
VMXERR_ENTRY_EVENTS_BLOCKED_BY_MOV_SS = 26,
VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID = 28,
};
#define SAVE_GPR \
"xchg %rax, regs\n\t" \
"xchg %rbx, regs+0x8\n\t" \
@ -451,10 +503,14 @@ enum Intr_type {
#define VMX_TEST_VMEXIT 1
#define VMX_TEST_EXIT 2
#define VMX_TEST_RESUME 3
#define VMX_TEST_VMABORT 4
#define VMX_TEST_VMSKIP 5
#define HYPERCALL_BIT (1ul << 12)
#define HYPERCALL_MASK 0xFFF
#define HYPERCALL_VMEXIT 0x1
#define HYPERCALL_VMABORT 0x2
#define HYPERCALL_VMSKIP 0x3
#define EPTP_PG_WALK_LEN_SHIFT 3ul
#define EPTP_AD_FLAG (1ul << 6)
@ -487,8 +543,10 @@ enum Intr_type {
#define EPT_CAP_INVEPT_ALL (1ull << 26)
#define EPT_CAP_AD_FLAG (1ull << 21)
#define VPID_CAP_INVVPID (1ull << 32)
#define VPID_CAP_INVVPID_SINGLE (1ull << 41)
#define VPID_CAP_INVVPID_ALL (1ull << 42)
#define VPID_CAP_INVVPID_ADDR (1ull << 40)
#define VPID_CAP_INVVPID_CXTGLB (1ull << 41)
#define VPID_CAP_INVVPID_ALL (1ull << 42)
#define VPID_CAP_INVVPID_CXTLOC (1ull << 43)
#define PAGE_SIZE_2M (512 * PAGE_SIZE)
#define PAGE_SIZE_1G (512 * PAGE_SIZE_2M)
@ -506,19 +564,23 @@ enum Intr_type {
#define EPT_VLT_PERM_RD (1 << 3)
#define EPT_VLT_PERM_WR (1 << 4)
#define EPT_VLT_PERM_EX (1 << 5)
#define EPT_VLT_PERMS (EPT_VLT_PERM_RD | EPT_VLT_PERM_WR | \
EPT_VLT_PERM_EX)
#define EPT_VLT_LADDR_VLD (1 << 7)
#define EPT_VLT_PADDR (1 << 8)
#define MAGIC_VAL_1 0x12345678ul
#define MAGIC_VAL_2 0x87654321ul
#define MAGIC_VAL_3 0xfffffffful
#define MAGIC_VAL_4 0xdeadbeeful
#define INVEPT_SINGLE 1
#define INVEPT_GLOBAL 2
#define INVVPID_SINGLE_ADDRESS 0
#define INVVPID_SINGLE 1
#define INVVPID_ADDR 0
#define INVVPID_CONTEXT_GLOBAL 1
#define INVVPID_ALL 2
#define INVVPID_CONTEXT_LOCAL 3
#define ACTV_ACTIVE 0
#define ACTV_HLT 1
@ -532,10 +594,41 @@ extern union vmx_ctrl_msr ctrl_exit_rev;
extern union vmx_ctrl_msr ctrl_enter_rev;
extern union vmx_ept_vpid ept_vpid;
extern u64 *vmxon_region;
void vmx_set_test_stage(u32 s);
u32 vmx_get_test_stage(void);
void vmx_inc_test_stage(void);
static int vmx_on(void)
{
bool ret;
u64 rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
asm volatile ("push %1; popf; vmxon %2; setbe %0\n\t"
: "=q" (ret) : "q" (rflags), "m" (vmxon_region) : "cc");
return ret;
}
static int vmx_off(void)
{
bool ret;
u64 rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
asm volatile("push %1; popf; vmxoff; setbe %0\n\t"
: "=q"(ret) : "q" (rflags) : "cc");
return ret;
}
static inline int make_vmcs_current(struct vmcs *vmcs)
{
bool ret;
u64 rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
asm volatile ("push %1; popf; vmptrld %2; setbe %0"
: "=q" (ret) : "q" (rflags), "m" (vmcs) : "cc");
return ret;
}
static inline int vmcs_clear(struct vmcs *vmcs)
{
bool ret;
@ -553,6 +646,25 @@ static inline u64 vmcs_read(enum Encoding enc)
return val;
}
static inline int vmcs_read_checking(enum Encoding enc, u64 *value)
{
u64 rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
u64 encoding = enc;
u64 val;
asm volatile ("shl $8, %%rax;"
"sahf;"
"vmread %[encoding], %[val];"
"lahf;"
"shr $8, %%rax"
: /* output */ [val]"=rm"(val), "+a"(rflags)
: /* input */ [encoding]"r"(encoding)
: /* clobber */ "cc");
*value = val;
return rflags & (X86_EFLAGS_CF | X86_EFLAGS_ZF);
}
static inline int vmcs_write(enum Encoding enc, u64 val)
{
bool ret;
@ -564,10 +676,12 @@ static inline int vmcs_write(enum Encoding enc, u64 val)
static inline int vmcs_save(struct vmcs **vmcs)
{
bool ret;
unsigned long pa;
u64 rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
asm volatile ("push %1; popf; vmptrst %2; setbe %0"
: "=q" (ret) : "q" (rflags), "m" (*vmcs) : "cc");
asm volatile ("push %2; popf; vmptrst %1; setbe %0"
: "=q" (ret), "=m" (pa) : "r" (rflags) : "cc");
*vmcs = (pa == -1ull) ? NULL : phys_to_virt(pa);
return ret;
}
@ -584,21 +698,18 @@ static inline bool invept(unsigned long type, u64 eptp)
return ret;
}
static inline bool invvpid(unsigned long type, u16 vpid, u64 gva)
static inline bool invvpid(unsigned long type, u64 vpid, u64 gla)
{
bool ret;
u64 rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
struct {
u64 vpid : 16;
u64 rsvd : 48;
u64 gva;
} operand = {vpid, 0, gva};
struct invvpid_operand operand = {vpid, gla};
asm volatile("push %1; popf; invvpid %2, %3; setbe %0"
: "=q" (ret) : "r" (rflags), "m"(operand),"r"(type) : "cc");
return ret;
}
const char *exit_reason_description(u64 reason);
void print_vmexit_info();
void print_vmentry_failure_info(struct vmentry_failure *failure);
void ept_sync(int type, u64 eptp);
@ -614,9 +725,83 @@ void install_ept(unsigned long *pml4, unsigned long phys,
unsigned long guest_addr, u64 perm);
void setup_ept_range(unsigned long *pml4, unsigned long start,
unsigned long len, int map_1g, int map_2m, u64 perm);
unsigned long get_ept_pte(unsigned long *pml4,
unsigned long guest_addr, int level);
int set_ept_pte(unsigned long *pml4, unsigned long guest_addr,
bool get_ept_pte(unsigned long *pml4, unsigned long guest_addr, int level,
unsigned long *pte);
void set_ept_pte(unsigned long *pml4, unsigned long guest_addr,
int level, u64 pte_val);
void check_ept_ad(unsigned long *pml4, u64 guest_cr3,
unsigned long guest_addr, int expected_gpa_ad,
int expected_pt_ad);
void clear_ept_ad(unsigned long *pml4, u64 guest_cr3,
unsigned long guest_addr);
bool ept_2m_supported(void);
bool ept_1g_supported(void);
bool ept_huge_pages_supported(int level);
bool ept_execute_only_supported(void);
bool ept_ad_bits_supported(void);
void enter_guest(void);
typedef void (*test_guest_func)(void);
typedef void (*test_teardown_func)(void *data);
void test_set_guest(test_guest_func func);
void test_add_teardown(test_teardown_func func, void *data);
void test_skip(const char *msg);
void __abort_test(void);
#define TEST_ASSERT(cond) \
do { \
if (!(cond)) { \
report("%s:%d: Assertion failed: %s", 0, \
__FILE__, __LINE__, #cond); \
dump_stack(); \
__abort_test(); \
} \
report_pass(); \
} while (0)
#define TEST_ASSERT_MSG(cond, fmt, args...) \
do { \
if (!(cond)) { \
report("%s:%d: Assertion failed: %s\n" fmt, 0, \
__FILE__, __LINE__, #cond, ##args); \
dump_stack(); \
__abort_test(); \
} \
report_pass(); \
} while (0)
#define __TEST_EQ(a, b, a_str, b_str, assertion, fmt, args...) \
do { \
typeof(a) _a = a; \
typeof(b) _b = b; \
if (_a != _b) { \
char _bin_a[BINSTR_SZ]; \
char _bin_b[BINSTR_SZ]; \
binstr(_a, _bin_a); \
binstr(_b, _bin_b); \
report("%s:%d: %s failed: (%s) == (%s)\n" \
"\tLHS: %#018lx - %s - %lu\n" \
"\tRHS: %#018lx - %s - %lu%s" fmt, 0, \
__FILE__, __LINE__, \
assertion ? "Assertion" : "Expectation", a_str, b_str, \
(unsigned long) _a, _bin_a, (unsigned long) _a, \
(unsigned long) _b, _bin_b, (unsigned long) _b, \
fmt[0] == '\0' ? "" : "\n", ## args); \
dump_stack(); \
if (assertion) \
__abort_test(); \
} \
report_pass(); \
} while (0)
#define TEST_ASSERT_EQ(a, b) __TEST_EQ(a, b, #a, #b, 1, "")
#define TEST_ASSERT_EQ_MSG(a, b, fmt, args...) \
__TEST_EQ(a, b, #a, #b, 1, fmt, ## args)
#define TEST_EXPECT_EQ(a, b) __TEST_EQ(a, b, #a, #b, 0, "")
#define TEST_EXPECT_EQ_MSG(a, b, fmt, args...) \
__TEST_EQ(a, b, #a, #b, 0, fmt, ## args)
#endif

File diff suppressed because it is too large Load diff

View file

@ -75,7 +75,7 @@ void test_xsave(void)
printf("Legal instruction testing:\n");
supported_xcr0 = get_supported_xcr0();
printf("Supported XCR0 bits: 0x%lx\n", supported_xcr0);
printf("Supported XCR0 bits: %#lx\n", supported_xcr0);
test_bits = XSTATE_FP | XSTATE_SSE;
report("Check minimal XSAVE required bits",