From 680688040fc26d17a49a9663fbbd6a716c6247b6 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 19 Nov 2020 21:01:35 +1100 Subject: pkg/ifuzz/powerpc: add powerpc support This adds KVM's syz_kvm_setup_cpu pseudo syscall. This adds placeholder for options (none implemented yet). This adds instruction generator for ifuzz; this also adds a few pseudo instructions to simulate super/hyper/ultracalls (a PPC64/pseries platform thing). The insns.go is generated from PowerISA_public.v3.0B.pdf [1] by a horrendous python3 script on top of pdftotext. The ISA covers POWER9 which is the latest available POWER CPU at the moment. The next ISA for POWER10 is quite different and we will deal with it later. The // comment after every instruction is a fixed opcode list for verification purposes. This does not define DecodeExt as there is no obvious replacement of the Intel XED library for POWERPC (gapstone-capstone, later, may be). [1] https://openpowerfoundation.org/?resource_lib=power-isa-version-3-0 Signed-off-by: Alexey Kardashevskiy --- executor/common_kvm_ppc64.h | 77 +++++++++++++++++++++++++++++++++++++++++++++ executor/common_linux.h | 2 ++ executor/test.h | 4 +-- executor/test_linux.h | 8 +++++ 4 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 executor/common_kvm_ppc64.h (limited to 'executor') diff --git a/executor/common_kvm_ppc64.h b/executor/common_kvm_ppc64.h new file mode 100644 index 000000000..f03ce42df --- /dev/null +++ b/executor/common_kvm_ppc64.h @@ -0,0 +1,77 @@ +// Copyright 2020 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +// This file is shared between executor and csource package. + +// Implementation of syz_kvm_setup_cpu pseudo-syscall. +// See Intel Software Developer’s Manual Volume 3: System Programming Guide +// for details on what happens here. + +#include "kvm.h" + +struct kvm_text { + uintptr_t typ; + const void* text; + uintptr_t size; +}; + +// syz_kvm_setup_cpu(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text, 1]], ntext len[text], flags flags[kvm_setup_flags], opts ptr[in, array[kvm_setup_opt, 0:2]], nopt len[opts]) +static long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) +{ + const int vmfd = a0; + const int cpufd = a1; + char* const host_mem = (char*)a2; + const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3; + const uintptr_t text_count = a4; + + const uintptr_t page_size = 16 << 10; + const uintptr_t guest_mem_size = 256 << 20; + const uintptr_t guest_mem = 0; + + (void)text_count; // fuzzer can spoof count and we need just 1 text, so ignore text_count + const void* text = 0; + uintptr_t text_size = 0; + NONFAILING(text = text_array_ptr[0].text); + NONFAILING(text_size = text_array_ptr[0].size); + + for (uintptr_t i = 0; i < guest_mem_size / page_size; i++) { + struct kvm_userspace_memory_region memreg; + memreg.slot = i; + memreg.flags = 0; // can be KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY + memreg.guest_phys_addr = guest_mem + i * page_size; + memreg.memory_size = page_size; + memreg.userspace_addr = (uintptr_t)host_mem + i * page_size; + ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg); + } + + struct kvm_regs regs; + struct kvm_sregs sregs; + if (ioctl(cpufd, KVM_GET_SREGS, &sregs)) + return -1; + if (ioctl(cpufd, KVM_GET_REGS, ®s)) + return -1; + + // PPC64LE, real mode: MSR_LE | MSR_SF + regs.msr = 1ULL | (1ULL << 63); + + memcpy(host_mem, text, text_size); + + if (ioctl(cpufd, KVM_SET_SREGS, &sregs)) + return -1; + if (ioctl(cpufd, KVM_SET_REGS, ®s)) + return -1; + + // Hypercalls need to be enable so we enable them all here to + // allow fuzzing +#define MAX_HCALL 0x450 + for (unsigned hcall = 4; hcall < MAX_HCALL; hcall += 4) { + struct kvm_enable_cap cap = { + .cap = KVM_CAP_PPC_ENABLE_HCALL, + .flags = 0, + .args = {hcall, 1}, + }; + ioctl(vmfd, KVM_ENABLE_CAP, &cap); + } + + return 0; +} diff --git a/executor/common_linux.h b/executor/common_linux.h index acb3acde7..16b78e99a 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -2905,6 +2905,8 @@ error_clear_loop: #include "common_kvm_amd64.h" #elif GOARCH_arm64 #include "common_kvm_arm64.h" +#elif GOARCH_ppc64 || GOARCH_ppc64le +#include "common_kvm_ppc64.h" #elif !GOARCH_arm static long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { diff --git a/executor/test.h b/executor/test.h index bd30fb372..37d03b65a 100644 --- a/executor/test.h +++ b/executor/test.h @@ -1,7 +1,7 @@ // Copyright 2017 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. -#if GOOS_linux && GOARCH_amd64 +#if GOOS_linux && (GOARCH_amd64 | GOARCH_ppc64 | GOARCH_ppc64le) #include "test_linux.h" #endif @@ -208,7 +208,7 @@ static struct { {"test_copyin", test_copyin}, {"test_csum_inet", test_csum_inet}, {"test_csum_inet_acc", test_csum_inet_acc}, -#if GOOS_linux && GOARCH_amd64 +#if GOOS_linux && (GOARCH_amd64 || GOARCH_ppc64 || GOARCH_ppc64le) {"test_kvm", test_kvm}, #endif }; diff --git a/executor/test_linux.h b/executor/test_linux.h index fcd8d855e..428fba967 100644 --- a/executor/test_linux.h +++ b/executor/test_linux.h @@ -75,11 +75,13 @@ static int test_one(int text_type, const char* text, int text_size, int flags, u dump_cpu_state(cpufd, (char*)vm_mem); return 1; } +#ifdef GOARCH_amd64 if (check_rax && regs.rax != 0xbadc0de) { printf("wrong result: rax=0x%llx\n", (long long)regs.rax); dump_cpu_state(cpufd, (char*)vm_mem); return 1; } +#endif munmap(vm_mem, vm_mem_size); munmap(cpu_mem, cpu_mem_size); close(cpufd); @@ -108,6 +110,7 @@ static int test_kvm() const char text8[] = "\x66\xb8\xde\xc0\xad\x0b"; if ((res = test_one(8, text8, sizeof(text8) - 1, 0, KVM_EXIT_HLT, true))) return res; +#ifdef GOARCH_amd64 if ((res = test_one(8, text8, sizeof(text8) - 1, KVM_SETUP_VIRT86, KVM_EXIT_SHUTDOWN, true))) return res; if ((res = test_one(8, text8, sizeof(text8) - 1, KVM_SETUP_VIRT86 | KVM_SETUP_PAGING, KVM_EXIT_SHUTDOWN, true))) @@ -161,6 +164,7 @@ static int test_kvm() if ((res = test_one(32, text_rsm, sizeof(text_rsm) - 1, KVM_SETUP_SMM, KVM_EXIT_HLT, false))) return res; } +#endif return 0; } @@ -179,14 +183,17 @@ static unsigned host_kernel_version() return major * 100 + minor; } +#ifdef GOARCH_amd64 static void dump_seg(const char* name, struct kvm_segment* seg) { printf("%s: base=0x%llx limit=0x%x sel=0x%x type=%d p=%d dpl=%d, db=%d s=%d l=%d g=%d\n", name, seg->base, seg->limit, seg->selector, seg->type, seg->present, seg->dpl, seg->db, seg->s, seg->l, seg->g); } +#endif static void dump_cpu_state(int cpufd, char* vm_mem) { +#ifdef GOARCH_amd64 struct kvm_sregs sregs; if (ioctl(cpufd, KVM_GET_SREGS, &sregs)) { printf("KVM_GET_SREGS failed (%d)\n", errno); @@ -219,4 +226,5 @@ static void dump_cpu_state(int cpufd, char* vm_mem) ((long long*)vm_mem)[i], ((long long*)vm_mem)[i + 1], ((long long*)vm_mem)[i + 2], ((long long*)vm_mem)[i + 3]); } } +#endif } -- cgit mrf-deployment