Skip to content

Commit

Permalink
KVM: arm64: Add a buffer that can pass UBSan data from hyp/nVHE to ke…
Browse files Browse the repository at this point in the history
…rnel

Share a buffer between the kernel and the hyp/nVHE code by using the
macros from kvm_debug_buffer.h.
The hyp/nVHE code requires a write index which counts how many elements
have been writtens inside the buffer and the kernel requires a read
index which counts how many elements have been read from the buffer.
The write index and the buffer are shared with the kernel in read-only.

The kvm_debug_buffer_ind returns the reading and writing points of the
circular buffer and updates the reading index.

Data collected from UBSan handlers inside hyp/nVHE is stored in the
kvm_ubsan_buffer.
This buffer stores only UBSan data because it should not be preoccupied
by other mechanisms data structures and functionalities.

Also, for the moment the buffer is mapped inside .bss, where both the kernel
and the hyp/nVHE code have Read/Write rights, but in the future this will change
and the kernel will not be able to acess hyp/nVHE's .bss. At that point the buffer
will only need to be mapped in order for this patch to work.

Change-Id: I696409db1de629b082abfe4c7f6bf066f12b539f
Signed-off-by: Elena Petrova <lenaptr@google.com>
  • Loading branch information
George Popescu authored and intel-lab-lkp committed Jan 15, 2021
1 parent 9eaabf2 commit aba3219
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 1 deletion.
11 changes: 11 additions & 0 deletions arch/arm64/include/asm/assembler.h
Expand Up @@ -258,6 +258,17 @@ alternative_endif
ldr \dst, [\dst, \tmp]
.endm

/*
* @sym: The name of the per-cpu variable
* @reg: value to store
* @tmp1: scratch register
* @tmp2: scratch register
*/
.macro str_this_cpu sym, reg, tmp1, tmp2
adr_this_cpu \tmp1, \sym, \tmp2
str \reg, [\tmp1]
.endm

/*
* vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm)
*/
Expand Down
36 changes: 36 additions & 0 deletions arch/arm64/include/asm/kvm_debug_buffer.h
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2020 Google LLC
* Author: George Popescu <georgepope@google.com>
*/

#include <linux/percpu-defs.h>


#define KVM_DEBUG_BUFFER_SIZE 1000

#ifdef __KVM_NVHE_HYPERVISOR__
#define DEFINE_KVM_DEBUG_BUFFER(type_name, buffer_name, write_ind, size)\
DEFINE_PER_CPU(type_name, buffer_name)[size]; \
DEFINE_PER_CPU(unsigned long, write_ind) = 0;

#define DECLARE_KVM_DEBUG_BUFFER(type_name, buffer_name, write_ind, size)\
DECLARE_PER_CPU(type_name, buffer_name)[size]; \
DECLARE_PER_CPU(unsigned long, write_ind);
#else
#define DECLARE_KVM_DEBUG_BUFFER(type_name, buffer_name, write_ind, size)\
DECLARE_KVM_NVHE_PER_CPU(type_name, buffer_name)[size]; \
DECLARE_KVM_NVHE_PER_CPU(unsigned long, write_ind);
#endif //__KVM_NVHE_HYPERVISOR__

#ifdef __ASSEMBLY__
#include <asm/assembler.h>

.macro clear_buffer tmp1, tmp2, tmp3
mov \tmp1, 0
#ifdef CONFIG_UBSAN
str_this_cpu kvm_ubsan_buff_wr_ind, \tmp1, \tmp2, \tmp3
#endif //CONFIG_UBSAN
.endm

#endif
8 changes: 7 additions & 1 deletion arch/arm64/include/asm/kvm_host.h
Expand Up @@ -569,14 +569,20 @@ int kvm_test_age_hva(struct kvm *kvm, unsigned long hva);
void kvm_arm_halt_guest(struct kvm *kvm);
void kvm_arm_resume_guest(struct kvm *kvm);


#ifdef CONFIG_UBSAN
extern void __kvm_check_ubsan_buffer(void);
#else
static inline void __kvm_check_ubsan_buffer(void) {}
#endif /* CONFIG_UBSAN */
#define kvm_call_hyp_nvhe(f, ...) \
({ \
struct arm_smccc_res res; \
\
arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(f), \
##__VA_ARGS__, &res); \
WARN_ON(res.a0 != SMCCC_RET_SUCCESS); \
\
__kvm_check_ubsan_buffer(); \
res.a1; \
})

Expand Down
14 changes: 14 additions & 0 deletions arch/arm64/include/asm/kvm_ubsan.h
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2020 Google LLC
* Author: George Popescu <georgepope@google.com>
*/

#include <ubsan.h>

#define UBSAN_MAX_TYPE 6
#define KVM_UBSAN_BUFFER_SIZE 1000

struct kvm_ubsan_info {
int type;
};
2 changes: 2 additions & 0 deletions arch/arm64/kvm/Makefile
Expand Up @@ -4,6 +4,7 @@
#

ccflags-y += -I $(srctree)/$(src)
CFLAGS_kvm_ubsan_buffer.o += -I $(srctree)/lib/

KVM=../../../virt/kvm

Expand All @@ -24,4 +25,5 @@ kvm-y := $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o \
vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
vgic/vgic-its.o vgic/vgic-debug.o

kvm-$(CONFIG_UBSAN) += kvm_ubsan_buffer.o
kvm-$(CONFIG_KVM_ARM_PMU) += pmu-emul.o
9 changes: 9 additions & 0 deletions arch/arm64/kvm/arm.c
Expand Up @@ -1780,6 +1780,15 @@ static int init_hyp_mode(void)
goto out_err;
}
}
#ifdef CONFIG_UBSAN
/* required by ubsan to access the handlers structures fields */
err = create_hyp_mappings(kvm_ksym_ref(_data),
kvm_ksym_ref(__end_once), PAGE_HYP_RO);
if (err) {
kvm_err("Cannot map data section\n");
goto out_err;
}
#endif

/*
* Map Hyp percpu pages
Expand Down
4 changes: 4 additions & 0 deletions arch/arm64/kvm/hyp/nvhe/host.S
Expand Up @@ -8,6 +8,7 @@

#include <asm/assembler.h>
#include <asm/kvm_asm.h>
#include <asm/kvm_debug_buffer.h>
#include <asm/kvm_mmu.h>

.text
Expand All @@ -34,6 +35,9 @@ SYM_FUNC_START(__host_exit)
/* Store the host regs x18-x29, lr */
save_callee_saved_regs x0

/* when entering the host clear the buffers */
clear_buffer x4, x5, x6

/* Save the host context pointer in x29 across the function call */
mov x29, x0
bl handle_trap
Expand Down
23 changes: 23 additions & 0 deletions arch/arm64/kvm/hyp/nvhe/ubsan.c
Expand Up @@ -3,10 +3,33 @@
* Copyright 2020 Google LLC
* Author: George Popescu <georgepope@google.com>
*/
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/percpu-defs.h>
#include <linux/kvm_host.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_asm.h>
#include <asm/kvm_ubsan.h>
#include <asm/kvm_debug_buffer.h>
#include <kvm/arm_pmu.h>
#include <ubsan.h>

DEFINE_KVM_DEBUG_BUFFER(struct kvm_ubsan_info, kvm_ubsan_buffer,
kvm_ubsan_buff_wr_ind, KVM_UBSAN_BUFFER_SIZE);

static inline struct kvm_ubsan_info *kvm_ubsan_buffer_next_slot(void)
{
struct kvm_ubsan_info *res = NULL;
unsigned long write_ind = __this_cpu_read(kvm_ubsan_buff_wr_ind);
if (write_ind < KVM_UBSAN_BUFFER_SIZE) {
res = this_cpu_ptr(&kvm_ubsan_buffer[write_ind]);
++write_ind;
__this_cpu_write(kvm_ubsan_buff_wr_ind, write_ind);
}
return res;
}

void __ubsan_handle_add_overflow(void *_data, void *lhs, void *rhs) {}

void __ubsan_handle_sub_overflow(void *_data, void *lhs, void *rhs) {}
Expand Down
40 changes: 40 additions & 0 deletions arch/arm64/kvm/kvm_ubsan_buffer.c
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2020 Google LLC
* Author: George Popescu <georgepope@google.com>
*/

#include <linux/ctype.h>
#include <linux/types.h>
#include <asm/kvm_debug_buffer.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_asm.h>
#include <kvm/arm_pmu.h>

#include <ubsan.h>
#include <asm/kvm_ubsan.h>

DECLARE_KVM_DEBUG_BUFFER(struct kvm_ubsan_info, kvm_ubsan_buffer,
kvm_ubsan_buff_wr_ind, KVM_UBSAN_BUFFER_SIZE);


void iterate_kvm_ubsan_buffer(unsigned long left, unsigned long right)
{
unsigned long i;
struct kvm_ubsan_info *slot;

slot = (struct kvm_ubsan_info *) this_cpu_ptr_nvhe_sym(kvm_ubsan_buffer);
for (i = left; i < right; ++i) {
/* check ubsan data */
slot[i].type = 0;
}
}

void __kvm_check_ubsan_buffer(void)
{
unsigned long *write_ind;

write_ind = (unsigned long *) this_cpu_ptr_nvhe_sym(kvm_ubsan_buff_wr_ind);
iterate_kvm_ubsan_buffer(0, *write_ind);
}

0 comments on commit aba3219

Please sign in to comment.