Index: head/sys/amd64/include/vmm.h =================================================================== --- head/sys/amd64/include/vmm.h +++ head/sys/amd64/include/vmm.h @@ -546,6 +546,9 @@ struct vie { uint8_t inst[VIE_INST_SIZE]; /* instruction bytes */ uint8_t num_valid; /* size of the instruction */ + +/* The following fields are all zeroed upon restart. */ +#define vie_startzero num_processed uint8_t num_processed; uint8_t addrsize:4, opsize:4; /* address and operand sizes */ Index: head/sys/amd64/include/vmm_instruction_emul.h =================================================================== --- head/sys/amd64/include/vmm_instruction_emul.h +++ head/sys/amd64/include/vmm_instruction_emul.h @@ -105,6 +105,7 @@ uint64_t gla, int prot, uint64_t *gpa, int *is_fault); #endif /* _KERNEL */ +void vie_restart(struct vie *vie); void vie_init(struct vie *vie, const char *inst_bytes, int inst_length); /* Index: head/sys/amd64/vmm/vmm_instruction_emul.c =================================================================== --- head/sys/amd64/vmm/vmm_instruction_emul.c +++ head/sys/amd64/vmm/vmm_instruction_emul.c @@ -53,7 +53,9 @@ #include #include #include +#include #include +#include #include #include #define KASSERT(exp,msg) assert((exp)) @@ -1990,22 +1992,36 @@ return (0); } +/* + * Prepare a partially decoded vie for a 2nd attempt. + */ void -vie_init(struct vie *vie, const char *inst_bytes, int inst_length) +vie_restart(struct vie *vie) { - KASSERT(inst_length >= 0 && inst_length <= VIE_INST_SIZE, - ("%s: invalid instruction length (%d)", __func__, inst_length)); + _Static_assert( + offsetof(struct vie, inst) < offsetof(struct vie, vie_startzero) && + offsetof(struct vie, num_valid) < offsetof(struct vie, vie_startzero), + "restart should not erase instruction length or contents"); - bzero(vie, sizeof(struct vie)); + memset((char *)vie + offsetof(struct vie, vie_startzero), 0, + sizeof(*vie) - offsetof(struct vie, vie_startzero)); vie->base_register = VM_REG_LAST; vie->index_register = VM_REG_LAST; vie->segment_register = VM_REG_LAST; +} - if (inst_length) { - bcopy(inst_bytes, vie->inst, inst_length); - vie->num_valid = inst_length; - } +void +vie_init(struct vie *vie, const char *inst_bytes, int inst_length) +{ + KASSERT(inst_length >= 0 && inst_length <= VIE_INST_SIZE, + ("%s: invalid instruction length (%d)", __func__, inst_length)); + + vie_restart(vie); + memset(vie->inst, 0, sizeof(vie->inst)); + if (inst_length != 0) + memcpy(vie->inst, inst_bytes, inst_length); + vie->num_valid = inst_length; } #ifdef _KERNEL Index: head/usr.sbin/bhyve/bhyverun.c =================================================================== --- head/usr.sbin/bhyve/bhyverun.c +++ head/usr.sbin/bhyve/bhyverun.c @@ -80,6 +80,7 @@ #ifndef WITHOUT_CAPSICUM #include #endif +#include #include #include "bhyverun.h" @@ -746,12 +747,26 @@ static int vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { - int err, i; + int err, i, cs_d; struct vie *vie; + enum vm_cpu_mode mode; stats.vmexit_inst_emul++; vie = &vmexit->u.inst_emul.vie; + if (!vie->decoded) { + /* + * Attempt to decode in userspace as a fallback. This allows + * updating instruction decode in bhyve without rebooting the + * kernel (rapid prototyping), albeit with much slower + * emulation. + */ + vie_restart(vie); + mode = vmexit->u.inst_emul.paging.cpu_mode; + cs_d = vmexit->u.inst_emul.cs_d; + (void)vmm_decode_instruction(mode, cs_d, vie); + } + err = emulate_mem(ctx, *pvcpu, vmexit->u.inst_emul.gpa, vie, &vmexit->u.inst_emul.paging);