All of lore.kernel.org
 help / color / mirror / Atom feed
From: Cyril Hrubis <chrubis@suse.cz>
To: Martin Doucha <mdoucha@suse.cz>
Cc: ltp@lists.linux.it
Subject: Re: [LTP] [PATCH v2 2/3] KVM test infrastructure
Date: Thu, 14 Apr 2022 12:25:09 +0200	[thread overview]
Message-ID: <Ylf2hd82r1pLFlbd@yuki> (raw)
In-Reply-To: <20220331113324.14742-3-mdoucha@suse.cz>

Hi!
> diff --git a/testcases/kernel/kvm/include/kvm_guest.h b/testcases/kernel/kvm/include/kvm_guest.h
> new file mode 100644
> index 000000000..edb1cb66d
> --- /dev/null
> +++ b/testcases/kernel/kvm/include/kvm_guest.h
> @@ -0,0 +1,82 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2021 SUSE LLC <mdoucha@suse.cz>
> + *
> + * Minimal test library for KVM tests
> + */
> +
> +#ifndef KVM_GUEST_H_
> +#define KVM_GUEST_H_
> +
> +/*
> + * These constants must match the value of corresponding constants defined
> + * in tst_res_flags.h.
> + */
> +#define TPASS	0	/* Test passed flag */
> +#define TFAIL	1	/* Test failed flag */
> +#define TBROK	2	/* Test broken flag */
> +#define TWARN	4	/* Test warning flag */
> +#define TINFO	16	/* Test information flag */
> +#define TCONF	32	/* Test not appropriate for configuration */

The tst_res_flags.h header has been created so that there will be no
need to copy these definitions around like this. Why not just include
the header?

> +#define TST_TEST_TCONF(message) \
> +	void main(void) { tst_brk(TCONF, message); }
> +
> +#define INTERRUPT_COUNT 32
> +
> +typedef unsigned long size_t;
> +typedef long ssize_t;
> +
> +typedef signed char int8_t;
> +typedef unsigned char uint8_t;
> +typedef short int16_t;
> +typedef unsigned short uint16_t;
> +typedef int int32_t;
> +typedef unsigned int uint32_t;
> +typedef long long int64_t;
> +typedef unsigned long long uint64_t;
> +typedef unsigned long uintptr_t;
> +
> +#define NULL ((void *)0)
> +
> +void *memset(void *dest, int val, size_t size);
> +void *memzero(void *dest, size_t size);
> +void *memcpy(void *dest, const void *src, size_t size);
> +
> +char *strcpy(char *dest, const char *src);
> +char *strcat(char *dest, const char *src);
> +size_t strlen(const char *str);
> +
> +/* Exit the VM by looping on a HLT instruction forever */
> +void kvm_exit(void) __attribute__((noreturn));
> +
> +/* Exit the VM using the HLT instruction but allow resume */
> +void kvm_yield(void);
> +
> +void tst_res_(const char *file, const int lineno, int result,
> +	const char *message);
> +#define tst_res(result, msg) tst_res_(__FILE__, __LINE__, (result), (msg))
> +
> +void tst_brk_(const char *file, const int lineno, int result,
> +	const char *message) __attribute__((noreturn));
> +#define tst_brk(result, msg) tst_brk_(__FILE__, __LINE__, (result), (msg))

I think that it may be slightly better to name these kvm_res() and
kvm_brk() to make clear that these are used in the guest context and
that the implementation is actually different. I guess that it would
make sense to have all the functions called from the guest prefixed with
kvm_ just to make the boundary clearer.

> +void *tst_heap_alloc_aligned(size_t size, size_t align);
> +void *tst_heap_alloc(size_t size);
>
> +/* Arch dependent: */
> +
> +struct kvm_interrupt_frame;
> +
> +typedef int (*tst_interrupt_callback)(void *userdata,
> +	struct kvm_interrupt_frame *ifrm, unsigned long errcode);
> +
> +extern const char *tst_interrupt_names[INTERRUPT_COUNT];
> +
> +void tst_set_interrupt_callback(unsigned int vector,
> +	tst_interrupt_callback func, void *userdata);
> +
> +/* Get the instruction pointer from interrupt frame */
> +uintptr_t kvm_get_interrupt_ip(const struct kvm_interrupt_frame *ifrm);
> +
> +#endif /* KVM_GUEST_H_ */
> diff --git a/testcases/kernel/kvm/include/kvm_host.h b/testcases/kernel/kvm/include/kvm_host.h
> new file mode 100644
> index 000000000..52cc3f5e9
> --- /dev/null
> +++ b/testcases/kernel/kvm/include/kvm_host.h
> @@ -0,0 +1,115 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2021 SUSE LLC <mdoucha@suse.cz>
> + *
> + * KVM host library for setting up and running virtual machine tests. Tests
> + * can either use the default setup/run/host functions or use the advanced
> + * API to create customized VMs.
> + */
> +
> +/*
> + * Most basic usage:
> + *
> + * #include "kvm_test.h"
> + *
> + * #ifdef COMPILE_PAYLOAD
> + *
> + * void main(void)
> + * {
> + *	[VM guest code goes here]
> + * }
> + *
> + * #else
> + *
> + * [optional VM host setup/run/cleanup code goes here]
> + *
> + * static struct tst_test test = {
> + *	.test_all = tst_kvm_run,
> + *	.setup = tst_kvm_setup,
> + *	.cleanup = tst_kvm_cleanup,
> + * };
> + *
> + * #endif
> + */
> +
> +#ifndef KVM_HOST_H_
> +#define KVM_HOST_H_
> +
> +#include <inttypes.h>
> +#include <linux/kvm.h>
> +#include "kvm_common.h"
> +
> +#define VM_KERNEL_BASEADDR 0x1000
> +#define VM_RESET_BASEADDR 0xfffffff0
> +#define VM_RESET_CODE_SIZE 8
> +
> +#define MIN_FREE_RAM (10 * 1024 * 1024)
> +#define DEFAULT_RAM_SIZE (16 * 1024 * 1024)
> +
> +struct tst_kvm_instance {
> +	int vm_fd, vcpu_fd;
> +	struct kvm_run *vcpu_info;
> +	size_t vcpu_info_size;
> +	void *ram;
> +	struct tst_kvm_result *result;
> +};
> +
> +/* Test binary to be installed into the VM at VM_KERNEL_BASEADDR */
> +extern const char kvm_payload_start[], kvm_payload_end[];
> +
> +/* CPU reset code to be installed into the VM at VM_RESET_BASEADDR */
> +extern const unsigned char tst_kvm_reset_code[VM_RESET_CODE_SIZE];
> +
> +/* Default KVM test functions. */
> +void tst_kvm_setup(void);
> +void tst_kvm_run(void);
> +void tst_kvm_cleanup(void);
> +
> +/*
> + * Validate KVM guest test result (usually passed via result->result) and
> + * fail with TBROK if the value cannot be safely passed to tst_res() or
> + * tst_brk().
> + */
> +void tst_kvm_validate_result(int value);
> +
> +/*
> + * Allocate memory slot for the VM. The returned pointer is page-aligned
> + * so the actual requested base address is at ret[baseaddr % pagesize].
> + *
> + * The first argument is a VM file descriptor created by ioctl(KVM_CREATE_VM)
> + *
> + * The return value points to a guarded buffer and the user should not attempt
> + * to free() it. Any extra space added at the beginning or end for page
> + * alignment will be writable.
> + */
> +void *tst_kvm_alloc_memory(int vm, unsigned int slot, uint64_t baseaddr,
> +	size_t size, unsigned int flags);
> +
> +/*
> + * Find CPUIDs supported by KVM. x86_64 tests must set non-default CPUID,
> + * otherwise bootstrap will fail to initialize 64bit mode.
> + * Returns NULL if ioctl(KVM_GET_SUPPORTED_CPUID) is not supported.
> + *
> + * The argument is a file descriptor created by open("/dev/kvm")
> + */
> +struct kvm_cpuid2 *tst_kvm_get_cpuid(int sysfd);
> +
> +/*
> + * Initialize the given KVM instance structure. Creates new KVM virtual machine
> + * with 1 virtual CPU, allocates VM RAM (max. 4GB minus one page) and
> + * shared result structure. KVM memory slots 0 and 1 will be set by this
> + * function.
> + */
> +void tst_kvm_create_instance(struct tst_kvm_instance *inst, size_t ram_size);
> +
> +/*
> + * Execute the given KVM instance and print results.
> + */
> +void tst_kvm_run_instance(struct tst_kvm_instance *inst);
> +
> +/*
> + * Close the given KVM instance.
> + */
> +void tst_kvm_destroy_instance(struct tst_kvm_instance *inst);
> +
> +#endif /* KVM_HOST_H_ */
> diff --git a/testcases/kernel/kvm/include/kvm_test.h b/testcases/kernel/kvm/include/kvm_test.h
> new file mode 100644
> index 000000000..55989eaa5
> --- /dev/null
> +++ b/testcases/kernel/kvm/include/kvm_test.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2021 SUSE LLC <mdoucha@suse.cz>
> + *
> + * Minimal test library for KVM tests
> + */
> +
> +#ifndef KVM_TEST_H_
> +#define KVM_TEST_H_
> +
> +#ifdef COMPILE_PAYLOAD
> +
> +  #include "kvm_guest.h"
> +  #include "kvm_common.h"
> +
> +#else
> +
> +  #include "tst_test.h"
> +  #include "kvm_host.h"
> +
> +#endif /* COMPILE_PAYLOAD */
> +
> +#endif /* KVM_TEST_H_ */
> diff --git a/testcases/kernel/kvm/include/kvm_x86.h b/testcases/kernel/kvm/include/kvm_x86.h
> new file mode 100644
> index 000000000..4f3671135
> --- /dev/null
> +++ b/testcases/kernel/kvm/include/kvm_x86.h
> @@ -0,0 +1,157 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2021 SUSE LLC <mdoucha@suse.cz>
> + *
> + * x86-specific KVM helper functions and structures
> + */
> +
> +#ifndef KVM_X86_H_
> +#define KVM_X86_H_
> +
> +#include "kvm_test.h"
> +
> +/* Interrupts */
> +#define X86_INTR_COUNT 256
> +
> +#define INTR_ZERODIV 0
> +#define INTR_DEBUG 1
> +#define INTR_NMI 2
> +#define INTR_BREAKPOINT 3
> +#define INTR_OVERFLOW 4
> +#define INTR_BOUND_RANGE_EXC 5
> +#define INTR_BAD_OPCODE 6
> +#define INTR_DEVICE_ERROR 7
> +#define INTR_DOUBLE_FAULT 8
> +#define INTR_INVALID_TSS 10
> +#define INTR_SEGFAULT 11
> +#define INTR_STACK_FAULT 12
> +#define INTR_GPF 13
> +#define INTR_PAGE_FAULT 14
> +#define INTR_FPU_ERROR 16
> +#define INTR_ALIGNMENT_ERROR 17
> +#define INTR_MACHINE_CHECK 18
> +#define INTR_SIMD_ERROR 19
> +#define INTR_VIRT_ERROR 20
> +#define INTR_CPE 21
> +#define INTR_HV_INJECTION 28
> +#define INTR_VMM_COMM 29
> +#define INTR_SECURITY_ERROR 30
> +
> +
> +/* CPUID constants */
> +#define CPUID_GET_INPUT_RANGE 0x80000000
> +#define CPUID_GET_EXT_FEATURES 0x80000001
> +
> +
> +/* Model-specific CPU register constants */
> +#define MSR_EFER 0xc0000080
> +
> +#define EFER_SCE (1 << 0)	/* SYSCALL/SYSRET instructions enabled */
> +#define EFER_LME (1 << 8)	/* CPU is running in 64bit mode */
> +#define EFER_LMA (1 << 10)	/* CPU uses 64bit memory paging (read-only) */
> +#define EFER_NXE (1 << 11)	/* Execute disable bit active */
> +
> +
> +/* Control register constants */
> +#define CR4_VME (1 << 0)
> +#define CR4_PVI (1 << 1)
> +#define CR4_TSD (1 << 2)
> +#define CR4_DE (1 << 3)
> +#define CR4_PSE (1 << 4)
> +#define CR4_PAE (1 << 5)
> +#define CR4_MCE (1 << 6)
> +#define CR4_PGE (1 << 7)
> +#define CR4_PCE (1 << 8)
> +#define CR4_OSFXSR (1 << 9)
> +#define CR4_OSXMMEXCPT (1 << 10)
> +#define CR4_UMIP (1 << 11)
> +#define CR4_LA57 (1 << 12)
> +#define CR4_VMXE (1 << 13)
> +#define CR4_SMXE (1 << 14)
> +#define CR4_FSGSBASE (1 << 16)
> +#define CR4_PCIDE (1 << 17)
> +#define CR4_OSXSAVE (1 << 18)
> +#define CR4_KL (1 << 19)
> +#define CR4_SMEP (1 << 20)
> +#define CR4_SMAP (1 << 21)
> +#define CR4_PKE (1 << 22)
> +#define CR4_CET (1 << 23)
> +#define CR4_PKS (1 << 24)
> +
> +struct intr_descriptor {
> +	uint16_t offset_lo;
> +	uint16_t selector;
> +	uint8_t ist;
> +	uint8_t flags;
> +#if defined(__x86_64__)
> +	uint64_t offset_hi; /* top 16 bits must be set to 0 */
> +	uint16_t padding;
> +#else /* defined(__x86_64__) */
> +	uint16_t offset_hi;
> +#endif /* defined(__x86_64__) */
> +} __attribute__((__packed__));
> +
> +struct page_table_entry_pae {
> +	unsigned int present: 1;
> +	unsigned int writable: 1;
> +	unsigned int user_access: 1;
> +	unsigned int write_through: 1;
> +	unsigned int disable_cache: 1;
> +	unsigned int accessed: 1;
> +	unsigned int dirty: 1;
> +	unsigned int page_type: 1;
> +	unsigned int global: 1;
> +	unsigned int padding: 3;
> +	uint64_t address: 40;
> +	unsigned int padding2: 7;
> +	unsigned int prot_key: 4;
> +	unsigned int noexec: 1;
> +} __attribute__((__packed__));
> +
> +struct kvm_cpuid {
> +	unsigned int eax, ebx, ecx, edx;
> +};
> +
> +struct kvm_cregs {
> +	unsigned long cr0, cr2, cr3, cr4;
> +};
> +
> +extern struct page_table_entry_pae kvm_pagetable[];
> +extern struct intr_descriptor kvm_idt[X86_INTR_COUNT];
> +
> +/* Page table helper functions */
> +uintptr_t kvm_get_page_address_pae(const struct page_table_entry_pae *entry);
> +
> +/* Functions for querying CPU info and status */
> +void kvm_get_cpuid(unsigned int eax, unsigned int ecx, struct kvm_cpuid *buf);
> +void kvm_read_cregs(struct kvm_cregs *buf);
> +uint64_t kvm_rdmsr(unsigned int msr);
> +void kvm_wrmsr(unsigned int msr, uint64_t value);
> +
> +/* Low-level interrupt handlers, DO NOT call directly */
> +void kvm_handle_bad_exception(void);
> +void kvm_handle_zerodiv(void);
> +void kvm_handle_debug(void);
> +void kvm_handle_nmi(void);
> +void kvm_handle_breakpoint(void);
> +void kvm_handle_overflow(void);
> +void kvm_handle_bound_range_exc(void);
> +void kvm_handle_bad_opcode(void);
> +void kvm_handle_device_error(void);
> +void kvm_handle_double_fault(void);
> +void kvm_handle_invalid_tss(void);
> +void kvm_handle_segfault(void);
> +void kvm_handle_stack_fault(void);
> +void kvm_handle_gpf(void);
> +void kvm_handle_page_fault(void);
> +void kvm_handle_fpu_error(void);
> +void kvm_handle_alignment_error(void);
> +void kvm_handle_machine_check(void);
> +void kvm_handle_simd_error(void);
> +void kvm_handle_virt_error(void);
> +void kvm_handle_cpe(void);
> +void kvm_handle_hv_injection(void);
> +void kvm_handle_vmm_comm(void);
> +void kvm_handle_security_error(void);
> +
> +#endif /* KVM_X86_H_ */
> diff --git a/testcases/kernel/kvm/lib_guest.c b/testcases/kernel/kvm/lib_guest.c
> new file mode 100644
> index 000000000..f5dfbc46f
> --- /dev/null
> +++ b/testcases/kernel/kvm/lib_guest.c
> @@ -0,0 +1,183 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2021 SUSE LLC <mdoucha@suse.cz>
> + *
> + * Minimal testing library for KVM tests
> + */
> +
> +#include "kvm_test.h"
> +
> +extern char kvm_heap_begin[];
> +
> +static struct tst_kvm_result *const test_result =
> +	(struct tst_kvm_result *)KVM_RESULT_BASEADDR;
> +
> +static char *heap_end = kvm_heap_begin;
> +
> +static struct tst_intr_handler {
> +	tst_interrupt_callback callback;
> +	void *userdata;
> +} intr_handlers[INTERRUPT_COUNT];
> +
> +void *memset(void *dest, int val, size_t size)
> +{
> +	char *ptr = dest;
> +
> +	while (size--)
> +		*ptr++ = val;
> +
> +	return dest;
> +}
> +
> +void *memzero(void *dest, size_t size)
> +{
> +	return memset(dest, 0, size);
> +}
> +
> +void *memcpy(void *dest, const void *src, size_t size)
> +{
> +	char *dptr = dest;
> +	const char *sptr = src;
> +
> +	while (size--)
> +		*dptr++ = *sptr++;
> +
> +	return dest;
> +}
> +
> +char *strcpy(char *dest, const char *src)
> +{
> +	char *ret = dest;
> +
> +	while ((*dest++ = *src++))
> +		;
> +
> +	return ret;
> +}
> +
> +char *strcat(char *dest, const char *src)
> +{
> +	char *ret = dest;
> +
> +	for (; *dest; dest++)
> +		;
> +
> +	strcpy(dest, src);
> +	return ret;
> +}
> +
> +size_t strlen(const char *str)
> +{
> +	size_t ret;
> +
> +	for (ret = 0; str[ret]; ret++)
> +		;
> +
> +	return ret;
> +}
> +
> +char *ptr2hex(char *dest, uintptr_t val)
> +{
> +	unsigned int i;
> +	uintptr_t tmp;
> +	char *ret = dest;
> +
> +	for (i = 4; val >> i; i += 4)
> +		;
> +
> +	do {
> +		i -= 4;
> +		tmp = (val >> i) & 0xf;
> +		*dest++ = tmp + (tmp >= 10 ? 'A' - 10 : '0');
> +	} while (i);
> +
> +	*dest = '\0';
> +	return ret;
> +}
> +
> +void *tst_heap_alloc_aligned(size_t size, size_t align)
> +{
> +	uintptr_t addr = (uintptr_t)heap_end;
> +	void *ret;
> +
> +	addr += align - 1;
> +	addr -= addr % align;
> +	ret = (void *)addr;
> +	addr += size + 3;
> +	addr -= addr % 4;
> +	heap_end = (char *)addr;
> +	return ret;
> +}
> +
> +void *tst_heap_alloc(size_t size)
> +{
> +	void *ret = heap_end;
> +
> +	size += 3;
> +	size -= size % 4;
> +	heap_end += size;
> +	return ret;

We do have a few similar pieces scattered around the code, can put the
code into a macro that would make the code slightly easier to
understand?

We do have LTP_ALIGN() in tst_common.h but I guess that we do not want
to include it here in the guest library.


Other than the very minor comments the code looks good.

-- 
Cyril Hrubis
chrubis@suse.cz

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

  parent reply	other threads:[~2022-04-14 10:23 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-31 11:33 [LTP] [PATCH v2 0/3] KVM testing Martin Doucha
2022-03-31 11:33 ` [LTP] [PATCH v2 1/3] Add AX_CHECK_COMPILE_FLAG() autoconf macro Martin Doucha
2022-04-01 22:04   ` Petr Vorel
2022-04-21 11:53   ` Cyril Hrubis
2022-03-31 11:33 ` [LTP] [PATCH v2 2/3] KVM test infrastructure Martin Doucha
2022-04-11  9:46   ` Richard Palethorpe
2022-04-14 10:25   ` Cyril Hrubis [this message]
2022-04-14 15:04     ` Martin Doucha
2022-04-20  9:11       ` Cyril Hrubis
2022-04-20  9:33         ` Martin Doucha
2022-04-20  9:52           ` Cyril Hrubis
2022-03-31 11:33 ` [LTP] [PATCH v2 3/3] Add test for CVE 2021-38198 Martin Doucha
2022-04-11  9:56   ` Richard Palethorpe

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Ylf2hd82r1pLFlbd@yuki \
    --to=chrubis@suse.cz \
    --cc=ltp@lists.linux.it \
    --cc=mdoucha@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.