summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2014-05-23 05:15:17 +0000
committerneel <neel@FreeBSD.org>2014-05-23 05:15:17 +0000
commit8f99933d8230d3e7da7e3faebeca2536fa117b6b (patch)
treebe2b310758f6bb7d7e91c4b4594bc520fe6c5898 /sys/amd64
parent062bfb5ea330c00e34b73aa892bccbd5ea82da22 (diff)
downloadFreeBSD-src-8f99933d8230d3e7da7e3faebeca2536fa117b6b.zip
FreeBSD-src-8f99933d8230d3e7da7e3faebeca2536fa117b6b.tar.gz
Add emulation of the "outsb" instruction. NetBSD guests use this to write to
the UART FIFO. The emulation is constrained in a number of ways: 64-bit only, doesn't check for all exception conditions, limited to i/o ports emulated in userspace. Some of these constraints will be relaxed in followup commits. Requested by: grehan Reviewed by: tychon (partially and a much earlier version)
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/include/vmm.h40
-rw-r--r--sys/amd64/include/vmm_instruction_emul.h10
-rw-r--r--sys/amd64/vmm/intel/vmx.c107
-rw-r--r--sys/amd64/vmm/vmm.c22
-rw-r--r--sys/amd64/vmm/vmm_instruction_emul.c48
-rw-r--r--sys/amd64/vmm/vmm_ioport.c181
-rw-r--r--sys/amd64/vmm/vmm_ioport.h2
-rw-r--r--sys/amd64/vmm/vmm_ktr.h4
8 files changed, 379 insertions, 35 deletions
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h
index 439f5aa..c759eee 100644
--- a/sys/amd64/include/vmm.h
+++ b/sys/amd64/include/vmm.h
@@ -54,6 +54,7 @@ struct vmspace;
struct vm_object;
struct pmap;
+enum vm_reg_name;
enum x2apic_state;
typedef int (*vmm_init_func_t)(int ipinum);
@@ -238,6 +239,8 @@ void vm_inject_gp(struct vm *vm, int vcpuid); /* general protection fault */
void vm_inject_ud(struct vm *vm, int vcpuid); /* undefined instruction fault */
void vm_inject_pf(struct vm *vm, int vcpuid, int error_code); /* page fault */
+enum vm_reg_name vm_segment_name(int seg_encoding);
+
#endif /* KERNEL */
#include <machine/vmm_instruction_emul.h>
@@ -336,22 +339,43 @@ enum vm_exitcode {
VM_EXITCODE_RENDEZVOUS,
VM_EXITCODE_IOAPIC_EOI,
VM_EXITCODE_SUSPENDED,
+ VM_EXITCODE_INOUT_STR,
VM_EXITCODE_MAX
};
+struct vm_inout {
+ uint16_t bytes:3; /* 1 or 2 or 4 */
+ uint16_t in:1;
+ uint16_t string:1;
+ uint16_t rep:1;
+ uint16_t port;
+ uint32_t eax; /* valid for out */
+};
+
+struct vm_inout_str {
+ struct vm_inout inout; /* must be the first element */
+ enum vie_cpu_mode cpu_mode;
+ enum vie_paging_mode paging_mode;
+ uint64_t rflags;
+ uint64_t cr0;
+ uint64_t cr3;
+ uint64_t index;
+ uint64_t count; /* rep=1 (%rcx), rep=0 (1) */
+ int cpl;
+ int addrsize;
+ enum vm_reg_name seg_name;
+ struct seg_desc seg_desc;
+ uint64_t gla; /* may be set to VIE_INVALID_GLA */
+ uint64_t gpa;
+};
+
struct vm_exit {
enum vm_exitcode exitcode;
int inst_length; /* 0 means unknown */
uint64_t rip;
union {
- struct {
- uint16_t bytes:3; /* 1 or 2 or 4 */
- uint16_t in:1; /* out is 0, in is 1 */
- uint16_t string:1;
- uint16_t rep:1;
- uint16_t port;
- uint32_t eax; /* valid for out */
- } inout;
+ struct vm_inout inout;
+ struct vm_inout_str inout_str;
struct {
uint64_t gpa;
int fault_type;
diff --git a/sys/amd64/include/vmm_instruction_emul.h b/sys/amd64/include/vmm_instruction_emul.h
index 02cb922..d2cfdef 100644
--- a/sys/amd64/include/vmm_instruction_emul.h
+++ b/sys/amd64/include/vmm_instruction_emul.h
@@ -29,6 +29,8 @@
#ifndef _VMM_INSTRUCTION_EMUL_H_
#define _VMM_INSTRUCTION_EMUL_H_
+enum vm_reg_name;
+
enum vie_cpu_mode {
CPU_MODE_COMPATIBILITY, /* IA-32E mode (CS.L = 0) */
CPU_MODE_64BIT, /* IA-32E mode (CS.L = 1) */
@@ -111,6 +113,9 @@ int vmm_emulate_instruction(void *vm, int cpuid, uint64_t gpa, struct vie *vie,
mem_region_read_t mrr, mem_region_write_t mrw,
void *mrarg);
+int vie_update_register(void *vm, int vcpuid, enum vm_reg_name reg,
+ uint64_t val, int size);
+
#ifdef _KERNEL
/*
* APIs to fetch and decode the instruction from nested page fault handler.
@@ -134,6 +139,11 @@ int vmm_gla2gpa(struct vm *vm, int vcpuid, uint64_t gla, uint64_t cr3,
void vie_init(struct vie *vie);
+uint64_t vie_size2mask(int size);
+
+uint64_t vie_segbase(enum vm_reg_name segment, enum vie_cpu_mode cpu_mode,
+ const struct seg_desc *desc);
+
/*
* Decode the instruction fetched into 'vie' so it can be emulated.
*
diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c
index 51f6615e..0cc1cc9 100644
--- a/sys/amd64/vmm/intel/vmx.c
+++ b/sys/amd64/vmm/intel/vmx.c
@@ -185,6 +185,8 @@ SYSCTL_UINT(_hw_vmm_vmx, OID_AUTO, vpid_alloc_failed, CTLFLAG_RD,
*/
#define APIC_ACCESS_ADDRESS 0xFFFFF000
+static int vmx_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc);
+static int vmx_getreg(void *arg, int vcpu, int reg, uint64_t *retval);
static void vmx_inject_pir(struct vlapic *vlapic);
#ifdef KTR
@@ -530,7 +532,7 @@ static int
vmx_init(int ipinum)
{
int error, use_tpr_shadow;
- uint64_t fixed0, fixed1, feature_control;
+ uint64_t basic, fixed0, fixed1, feature_control;
uint32_t tmp, procbased2_vid_bits;
/* CPUID.1:ECX[bit 5] must be 1 for processor to support VMX */
@@ -550,6 +552,17 @@ vmx_init(int ipinum)
return (ENXIO);
}
+ /*
+ * Verify capabilities MSR_VMX_BASIC:
+ * - bit 54 indicates support for INS/OUTS decoding
+ */
+ basic = rdmsr(MSR_VMX_BASIC);
+ if ((basic & (1UL << 54)) == 0) {
+ printf("vmx_init: processor does not support desired basic "
+ "capabilities\n");
+ return (EINVAL);
+ }
+
/* Check support for primary processor-based VM-execution controls */
error = vmx_set_ctlreg(MSR_VMX_PROCBASED_CTLS,
MSR_VMX_TRUE_PROCBASED_CTLS,
@@ -1528,6 +1541,71 @@ vmx_paging_mode(void)
return (PAGING_MODE_PAE);
}
+static uint64_t
+inout_str_index(struct vmx *vmx, int vcpuid, int in)
+{
+ uint64_t val;
+ int error;
+ enum vm_reg_name reg;
+
+ reg = in ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI;
+ error = vmx_getreg(vmx, vcpuid, reg, &val);
+ KASSERT(error == 0, ("%s: vmx_getreg error %d", __func__, error));
+ return (val);
+}
+
+static uint64_t
+inout_str_count(struct vmx *vmx, int vcpuid, int rep)
+{
+ uint64_t val;
+ int error;
+
+ if (rep) {
+ error = vmx_getreg(vmx, vcpuid, VM_REG_GUEST_RCX, &val);
+ KASSERT(!error, ("%s: vmx_getreg error %d", __func__, error));
+ } else {
+ val = 1;
+ }
+ return (val);
+}
+
+static int
+inout_str_addrsize(uint32_t inst_info)
+{
+ uint32_t size;
+
+ size = (inst_info >> 7) & 0x7;
+ switch (size) {
+ case 0:
+ return (2); /* 16 bit */
+ case 1:
+ return (4); /* 32 bit */
+ case 2:
+ return (8); /* 64 bit */
+ default:
+ panic("%s: invalid size encoding %d", __func__, size);
+ }
+}
+
+static void
+inout_str_seginfo(struct vmx *vmx, int vcpuid, uint32_t inst_info, int in,
+ struct vm_inout_str *vis)
+{
+ int error, s;
+
+ if (in) {
+ vis->seg_name = VM_REG_GUEST_ES;
+ } else {
+ s = (inst_info >> 15) & 0x7;
+ vis->seg_name = vm_segment_name(s);
+ }
+
+ error = vmx_getdesc(vmx, vcpuid, vis->seg_name, &vis->seg_desc);
+ KASSERT(error == 0, ("%s: vmx_getdesc error %d", __func__, error));
+
+ /* XXX modify svm.c to update bit 16 of seg_desc.access (unusable) */
+}
+
static void
vmexit_inst_emul(struct vm_exit *vmexit, uint64_t gpa, uint64_t gla)
{
@@ -1749,10 +1827,12 @@ vmx_handle_apic_access(struct vmx *vmx, int vcpuid, struct vm_exit *vmexit)
static int
vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
{
- int error, handled;
+ int error, handled, in;
struct vmxctx *vmxctx;
struct vlapic *vlapic;
- uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, reason;
+ struct vm_inout_str *vis;
+ uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, inst_info;
+ uint32_t reason;
uint64_t qual, gpa;
bool retu;
@@ -1909,15 +1989,26 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_INOUT, 1);
vmexit->exitcode = VM_EXITCODE_INOUT;
vmexit->u.inout.bytes = (qual & 0x7) + 1;
- vmexit->u.inout.in = (qual & 0x8) ? 1 : 0;
+ vmexit->u.inout.in = in = (qual & 0x8) ? 1 : 0;
vmexit->u.inout.string = (qual & 0x10) ? 1 : 0;
vmexit->u.inout.rep = (qual & 0x20) ? 1 : 0;
vmexit->u.inout.port = (uint16_t)(qual >> 16);
vmexit->u.inout.eax = (uint32_t)(vmxctx->guest_rax);
- error = emulate_ioport(vmx->vm, vcpu, vmexit);
- if (error == 0) {
- handled = 1;
- vmxctx->guest_rax = vmexit->u.inout.eax;
+ if (vmexit->u.inout.string) {
+ inst_info = vmcs_read(VMCS_EXIT_INSTRUCTION_INFO);
+ vmexit->exitcode = VM_EXITCODE_INOUT_STR;
+ vis = &vmexit->u.inout_str;
+ vis->cpu_mode = vmx_cpu_mode();
+ vis->paging_mode = vmx_paging_mode();
+ vis->rflags = vmcs_read(VMCS_GUEST_RFLAGS);
+ vis->cr0 = vmcs_read(VMCS_GUEST_CR0);
+ vis->cr3 = vmcs_read(VMCS_GUEST_CR3);
+ vis->cpl = vmx_cpl();
+ vis->index = inout_str_index(vmx, vcpu, in);
+ vis->count = inout_str_count(vmx, vcpu, vis->inout.rep);
+ vis->addrsize = inout_str_addrsize(inst_info);
+ inout_str_seginfo(vmx, vcpu, inst_info, in, vis);
+ vis->gla = vmcs_gla();
}
break;
case EXIT_REASON_CPUID:
diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c
index b009905..132af36 100644
--- a/sys/amd64/vmm/vmm.c
+++ b/sys/amd64/vmm/vmm.c
@@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$");
#include <machine/vmm.h>
#include <machine/vmm_dev.h>
+#include "vmm_ioport.h"
#include "vmm_ktr.h"
#include "vmm_host.h"
#include "vmm_mem.h"
@@ -1354,6 +1355,10 @@ restart:
case VM_EXITCODE_INST_EMUL:
error = vm_handle_inst_emul(vm, vcpuid, &retu);
break;
+ case VM_EXITCODE_INOUT:
+ case VM_EXITCODE_INOUT_STR:
+ error = vm_handle_inout(vm, vcpuid, vme, &retu);
+ break;
default:
retu = true; /* handled in userland */
break;
@@ -1874,3 +1879,20 @@ vm_atpit(struct vm *vm)
{
return (vm->vatpit);
}
+
+enum vm_reg_name
+vm_segment_name(int seg)
+{
+ static enum vm_reg_name seg_names[] = {
+ VM_REG_GUEST_ES,
+ VM_REG_GUEST_CS,
+ VM_REG_GUEST_SS,
+ VM_REG_GUEST_DS,
+ VM_REG_GUEST_FS,
+ VM_REG_GUEST_GS
+ };
+
+ KASSERT(seg >= 0 && seg < nitems(seg_names),
+ ("%s: invalid segment encoding %d", __func__, seg));
+ return (seg_names[seg]);
+}
diff --git a/sys/amd64/vmm/vmm_instruction_emul.c b/sys/amd64/vmm/vmm_instruction_emul.c
index d4c9264..8534369 100644
--- a/sys/amd64/vmm/vmm_instruction_emul.c
+++ b/sys/amd64/vmm/vmm_instruction_emul.c
@@ -206,7 +206,7 @@ vie_read_bytereg(void *vm, int vcpuid, struct vie *vie, uint8_t *rval)
return (error);
}
-static int
+int
vie_update_register(void *vm, int vcpuid, enum vm_reg_name reg,
uint64_t val, int size)
{
@@ -1218,4 +1218,50 @@ vmm_decode_instruction(struct vm *vm, int cpuid, uint64_t gla,
return (0);
}
+
+uint64_t
+vie_size2mask(int size)
+{
+ KASSERT(size == 1 || size == 2 || size == 4 || size == 8,
+ ("vie_size2mask: invalid size %d", size));
+ return (size2mask[size]);
+}
+
+uint64_t
+vie_segbase(enum vm_reg_name seg, enum vie_cpu_mode cpu_mode,
+ const struct seg_desc *desc)
+{
+ int basesize;
+
+ basesize = 4; /* default segment width in bytes */
+
+ switch (seg) {
+ case VM_REG_GUEST_ES:
+ case VM_REG_GUEST_CS:
+ case VM_REG_GUEST_SS:
+ case VM_REG_GUEST_DS:
+ if (cpu_mode == CPU_MODE_64BIT) {
+ /*
+ * Segments having an implicit base address of 0
+ * in 64-bit mode.
+ */
+ return (0);
+ }
+ break;
+ case VM_REG_GUEST_FS:
+ case VM_REG_GUEST_GS:
+ if (cpu_mode == CPU_MODE_64BIT) {
+ /*
+ * In 64-bit mode the FS and GS base address is 8 bytes
+ * wide.
+ */
+ basesize = 8;
+ }
+ break;
+ default:
+ panic("%s: invalid segment register %d", __func__, seg);
+ }
+
+ return (desc->base & size2mask[basesize]);
+}
#endif /* _KERNEL */
diff --git a/sys/amd64/vmm/vmm_ioport.c b/sys/amd64/vmm/vmm_ioport.c
index eae45cc..20f2498 100644
--- a/sys/amd64/vmm/vmm_ioport.c
+++ b/sys/amd64/vmm/vmm_ioport.c
@@ -33,11 +33,15 @@ __FBSDID("$FreeBSD$");
#include <sys/cpuset.h>
#include <sys/systm.h>
+#include <vm/vm.h>
+
#include <machine/vmm.h>
+#include <x86/psl.h>
#include "vatpic.h"
#include "vatpit.h"
#include "vmm_ioport.h"
+#include "vmm_ktr.h"
#define MAX_IOPORTS 1280
@@ -55,32 +59,64 @@ ioport_handler_func_t ioport_handler[MAX_IOPORTS] = {
[IO_ELCR2] = vatpic_elc_handler,
};
-int
-emulate_ioport(struct vm *vm, int vcpuid, struct vm_exit *vmexit)
+#ifdef KTR
+static const char *
+inout_instruction(struct vm_exit *vmexit)
{
- ioport_handler_func_t handler;
- uint32_t mask, val;
- int error;
+ int index;
- if (vmexit->u.inout.port >= MAX_IOPORTS)
- return (-1);
-
- handler = ioport_handler[vmexit->u.inout.port];
- if (handler == NULL)
- return (-1);
+ static const char *iodesc[] = {
+ "outb", "outw", "outl",
+ "inb", "inw", "inl",
+ "outsb", "outsw", "outsd"
+ "insb", "insw", "insd",
+ };
switch (vmexit->u.inout.bytes) {
case 1:
- mask = 0xff;
+ index = 0;
break;
case 2:
- mask = 0xffff;
+ index = 1;
break;
default:
- mask = 0xffffffff;
+ index = 2;
break;
}
+ if (vmexit->u.inout.in)
+ index += 3;
+
+ if (vmexit->u.inout.string)
+ index += 6;
+
+ KASSERT(index < nitems(iodesc), ("%s: invalid index %d",
+ __func__, index));
+
+ return (iodesc[index]);
+}
+#endif /* KTR */
+
+static int
+emulate_inout_port(struct vm *vm, int vcpuid, struct vm_exit *vmexit,
+ bool *retu)
+{
+ ioport_handler_func_t handler;
+ uint32_t mask, val;
+ int error;
+
+ error = 0;
+ *retu = true;
+
+ if (vmexit->u.inout.port >= MAX_IOPORTS)
+ goto done;
+
+ handler = ioport_handler[vmexit->u.inout.port];
+ if (handler == NULL)
+ goto done;
+
+ mask = vie_size2mask(vmexit->u.inout.bytes);
+
if (!vmexit->u.inout.in) {
val = vmexit->u.inout.eax & mask;
}
@@ -88,10 +124,121 @@ emulate_ioport(struct vm *vm, int vcpuid, struct vm_exit *vmexit)
error = (*handler)(vm, vcpuid, vmexit->u.inout.in,
vmexit->u.inout.port, vmexit->u.inout.bytes, &val);
- if (!error && vmexit->u.inout.in) {
- vmexit->u.inout.eax &= ~mask;
- vmexit->u.inout.eax |= val & mask;
+ if (!error) {
+ *retu = false;
+ if (vmexit->u.inout.in) {
+ vmexit->u.inout.eax &= ~mask;
+ vmexit->u.inout.eax |= val & mask;
+ error = vm_set_register(vm, vcpuid,
+ VM_REG_GUEST_RAX, vmexit->u.inout.eax);
+ KASSERT(error == 0, ("emulate_ioport: error %d "
+ "setting guest rax register", error));
+ }
+ }
+done:
+ return (error);
+}
+
+static int
+emulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
+{
+ struct vm_inout_str *vis;
+ uint64_t gla, index, segbase;
+ int bytes, error, in;
+
+ vis = &vmexit->u.inout_str;
+ in = vis->inout.in;
+
+ /*
+ * ins/outs VM exit takes precedence over the following error
+ * conditions that would ordinarily be checked by the processor:
+ *
+ * - #GP(0) due to segment being unusable.
+ * - #GP(0) due to memory operand effective address outside the limit
+ * of the segment.
+ * - #AC(0) if alignment checking is enabled and an unaligned memory
+ * reference is made at CPL=3
+ */
+
+ /*
+ * XXX
+ * inout string emulation only supported in 64-bit mode and only
+ * for byte instructions.
+ *
+ * The #GP(0) fault conditions described above don't apply in
+ * 64-bit mode.
+ *
+ * The #AC(0) fault condition described above does not apply
+ * because byte accesses don't have alignment constraints.
+ */
+ if (vis->cpu_mode != CPU_MODE_64BIT) {
+ VCPU_CTR1(vm, vcpuid, "ins/outs not emulated in cpu mode %d",
+ vis->cpu_mode);
+ return (EINVAL);
+ }
+
+ bytes = vis->inout.bytes;
+ if (bytes != 1) {
+ VCPU_CTR1(vm, vcpuid, "ins/outs operand size %d not supported",
+ bytes);
+ return (EINVAL);
+ }
+
+ /*
+ * XXX insb/insw/insd instructions not emulated at this time.
+ */
+ if (in) {
+ VCPU_CTR0(vm, vcpuid, "ins emulation not implemented");
+ return (EINVAL);
}
+ segbase = vie_segbase(vis->seg_name, vis->cpu_mode, &vis->seg_desc);
+ index = vis->index & vie_size2mask(vis->addrsize);
+ gla = segbase + index;
+
+ /*
+ * Verify that the computed linear address matches with the one
+ * provided by hardware.
+ */
+ if (vis->gla != VIE_INVALID_GLA) {
+ KASSERT(gla == vis->gla, ("%s: gla mismatch "
+ "%#lx/%#lx", __func__, gla, vis->gla));
+ }
+ vis->gla = gla;
+
+ error = vmm_gla2gpa(vm, vcpuid, gla, vis->cr3, &vis->gpa,
+ vis->paging_mode, vis->cpl, in ? VM_PROT_WRITE : VM_PROT_READ);
+ KASSERT(error == 0 || error == 1 || error == -1,
+ ("%s: vmm_gla2gpa unexpected error %d", __func__, error));
+ if (error == -1) {
+ return (EFAULT);
+ } else if (error == 1) {
+ return (0); /* Resume guest to handle page fault */
+ } else {
+ *retu = true;
+ return (0); /* Return to userspace to finish emulation */
+ }
+}
+
+int
+vm_handle_inout(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
+{
+ int bytes, error;
+
+ bytes = vmexit->u.inout.bytes;
+ KASSERT(bytes == 1 || bytes == 2 || bytes == 4,
+ ("vm_handle_inout: invalid operand size %d", bytes));
+
+ if (vmexit->u.inout.string)
+ error = emulate_inout_str(vm, vcpuid, vmexit, retu);
+ else
+ error = emulate_inout_port(vm, vcpuid, vmexit, retu);
+
+ VCPU_CTR4(vm, vcpuid, "%s%s 0x%04x: %s",
+ vmexit->u.inout.rep ? "rep " : "",
+ inout_instruction(vmexit),
+ vmexit->u.inout.port,
+ error ? "error" : (*retu ? "userspace" : "handled"));
+
return (error);
}
diff --git a/sys/amd64/vmm/vmm_ioport.h b/sys/amd64/vmm/vmm_ioport.h
index 02e543a..84a4cf1 100644
--- a/sys/amd64/vmm/vmm_ioport.h
+++ b/sys/amd64/vmm/vmm_ioport.h
@@ -32,6 +32,6 @@
typedef int (*ioport_handler_func_t)(void *vm, int vcpuid,
bool in, int port, int bytes, uint32_t *val);
-int emulate_ioport(struct vm *vm, int vcpuid, struct vm_exit *vmexit);
+int vm_handle_inout(struct vm *vm, int vcpuid, struct vm_exit *vme, bool *retu);
#endif /* _VMM_IOPORT_H_ */
diff --git a/sys/amd64/vmm/vmm_ktr.h b/sys/amd64/vmm/vmm_ktr.h
index 9fb46d8..61ff53f 100644
--- a/sys/amd64/vmm/vmm_ktr.h
+++ b/sys/amd64/vmm/vmm_ktr.h
@@ -48,6 +48,10 @@ CTR4(KTR_VMM, "vm %s[%d]: " format, vm_name((vm)), (vcpuid), (p1), (p2))
#define VCPU_CTR3(vm, vcpuid, format, p1, p2, p3) \
CTR5(KTR_VMM, "vm %s[%d]: " format, vm_name((vm)), (vcpuid), (p1), (p2), (p3))
+#define VCPU_CTR4(vm, vcpuid, format, p1, p2, p3, p4) \
+CTR6(KTR_VMM, "vm %s[%d]: " format, vm_name((vm)), (vcpuid), \
+ (p1), (p2), (p3), (p4))
+
#define VM_CTR0(vm, format) \
CTR1(KTR_VMM, "vm %s: " format, vm_name((vm)))
OpenPOWER on IntegriCloud